Tfx: Airflow webserver does not work with Python >=3.5

Created on 22 May 2019  路  12Comments  路  Source: tensorflow/tfx

When starting the Airflow web server at http://localhost:8080/admin/ (inside a virtual environment with Python 3), the following error message occurs:

Traceback (most recent call last):
  File "/Users/ian/taxi_pipeline/lib/python3.5/site-packages/sqlalchemy/engine/base.py", line 1236, in _execute_context
    cursor, statement, parameters, context
  File "/Users/ian/taxi_pipeline/lib/python3.5/site-packages/sqlalchemy/engine/default.py", line 536, in do_execute
    cursor.execute(statement, parameters)
sqlite3.OperationalError: no such column: dag.description

The problem is here:

sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such column: dag.description [SQL: 'SELECT dag.dag_id AS dag_dag_id, dag.is_paused AS dag_is_paused, dag.is_subdag AS dag_is_subdag, dag.is_active AS dag_is_active, dag.last_scheduler_run AS dag_last_scheduler_run, dag.last_pickled AS dag_last_pickled, dag.last_expired AS dag_last_expired, dag.scheduler_lock AS dag_scheduler_lock, dag.pickle_id AS dag_pickle_id, dag.fileloc AS dag_fileloc, dag.owners AS dag_owners, dag.description AS dag_description, dag.default_view AS dag_default_view, dag.schedule_interval AS dag_schedule_interval \nFROM dag \nWHERE dag.is_subdag = 0 AND dag.is_active = 1 ORDER BY dag.dag_id\n LIMIT ? OFFSET ?'] [parameters: (100, 0)] (Background on this error at: http://sqlalche.me/e/e3q8)

To reproduce, I ran the following commands according to the instructions:

cd ~
rm -rf taxi && rm -rf taxi_pipeline && rm -rf airflow && rm -rf tfx
git clone https://github.com/tensorflow/tfx.git
cd tfx && git checkout tags/0.13.0 -b release

cd ~
virtualenv -p python3.5 taxi_pipeline
source ./taxi_pipeline/bin/activate

export AIRFLOW_HOME=~/airflow
export TAXI_DIR=~/taxi
export TFX_DIR=~/tfx

pip install tensorflow==1.13.1
pip install docker
export SLUGIFY_USES_TEXT_UNIDECODE=yes
pip install apache-airflow
pip install tfx==0.13.0

airflow initdb

cd $TFX_DIR/tfx/examples/chicago_taxi_pipeline

mkdir -p $TAXI_DIR/data/simple
cp data/simple/data.csv $TAXI_DIR/data/simple

mkdir -p $AIRFLOW_HOME/dags/taxi
cp taxi_pipeline_simple.py $AIRFLOW_HOME/dags/taxi

cp taxi_utils.py $TAXI_DIR

airflow webserver

I see the same behaviour for a Python 3.7 virtual environment. Oddly, enough I get the same error now for 2.7!

I assumed it was a version incompatibility with Airflow, so I fixed the version to 1.10.2, but that yields:

Traceback (most recent call last):
  File "/Users/ian/taxi_pipeline/lib/python3.7/site-packages/flask/app.py", line 1982, in wsgi_app
    response = self.full_dispatch_request()
  File "/Users/ian/taxi_pipeline/lib/python3.7/site-packages/flask/app.py", line 1614, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/Users/ian/taxi_pipeline/lib/python3.7/site-packages/flask/app.py", line 1517, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/Users/ian/taxi_pipeline/lib/python3.7/site-packages/flask/_compat.py", line 33, in reraise
    raise value
  File "/Users/ian/taxi_pipeline/lib/python3.7/site-packages/flask/app.py", line 1612, in full_dispatch_request
    rv = self.dispatch_request()
  File "/Users/ian/taxi_pipeline/lib/python3.7/site-packages/flask/app.py", line 1598, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/Users/ian/taxi_pipeline/lib/python3.7/site-packages/flask_admin/base.py", line 69, in inner
    return self._run_view(f, *args, **kwargs)
  File "/Users/ian/taxi_pipeline/lib/python3.7/site-packages/flask_admin/base.py", line 368, in _run_view
    return fn(self, *args, **kwargs)
  File "/Users/ian/taxi_pipeline/lib/python3.7/site-packages/flask_login/utils.py", line 258, in decorated_view
    return func(*args, **kwargs)
  File "/Users/ian/taxi_pipeline/lib/python3.7/site-packages/airflow/utils/db.py", line 73, in wrapper
    return func(*args, **kwargs)
  File "/Users/ian/taxi_pipeline/lib/python3.7/site-packages/airflow/www/views.py", line 2224, in index
    auto_complete_data=auto_complete_data)
  File "/Users/ian/taxi_pipeline/lib/python3.7/site-packages/flask_admin/base.py", line 308, in render
    return render_template(template, **kwargs)
  File "/Users/ian/taxi_pipeline/lib/python3.7/site-packages/flask/templating.py", line 134, in render_template
    context, ctx.app)
  File "/Users/ian/taxi_pipeline/lib/python3.7/site-packages/flask/templating.py", line 116, in _render
    rv = template.render(context)
  File "/Users/ian/taxi_pipeline/lib/python3.7/site-packages/jinja2/asyncsupport.py", line 76, in render
    return original_render(self, *args, **kwargs)
  File "/Users/ian/taxi_pipeline/lib/python3.7/site-packages/jinja2/environment.py", line 1008, in render
    return self.environment.handle_exception(exc_info, True)
  File "/Users/ian/taxi_pipeline/lib/python3.7/site-packages/jinja2/environment.py", line 780, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/Users/ian/taxi_pipeline/lib/python3.7/site-packages/jinja2/_compat.py", line 37, in reraise
    raise value.with_traceback(tb)
  File "/Users/ian/taxi_pipeline/lib/python3.7/site-packages/airflow/www/templates/airflow/dags.html", line 18, in top-level template code
    {% extends "airflow/master.html" %}
  File "/Users/ian/taxi_pipeline/lib/python3.7/site-packages/airflow/www/templates/airflow/master.html", line 18, in top-level template code
    {% extends "admin/master.html" %}
  File "/Users/ian/taxi_pipeline/lib/python3.7/site-packages/airflow/www/templates/admin/master.html", line 18, in top-level template code
    {% extends 'admin/base.html' %}
  File "/Users/ian/taxi_pipeline/lib/python3.7/site-packages/flask_admin/templates/bootstrap3/admin/base.html", line 81, in top-level template code
    {% block tail_js %}
  File "/Users/ian/taxi_pipeline/lib/python3.7/site-packages/airflow/www/templates/admin/master.html", line 44, in block "tail_js"
    xhr.setRequestHeader("X-CSRFToken", "{{ csrf_token() }}");
  File "/Users/ian/taxi_pipeline/lib/python3.7/site-packages/flask_wtf/csrf.py", line 47, in generate_csrf
    setattr(g, field_name, s.dumps(session[field_name]))
  File "/Users/ian/taxi_pipeline/lib/python3.7/site-packages/itsdangerous/serializer.py", line 166, in dumps
    payload = want_bytes(self.dump_payload(obj))
  File "/Users/ian/taxi_pipeline/lib/python3.7/site-packages/itsdangerous/url_safe.py", line 42, in dump_payload
    json = super(URLSafeSerializerMixin, self).dump_payload(obj)
  File "/Users/ian/taxi_pipeline/lib/python3.7/site-packages/itsdangerous/serializer.py", line 133, in dump_payload
    return want_bytes(self.serializer.dumps(obj, **self.serializer_kwargs))
  File "/Users/ian/taxi_pipeline/lib/python3.7/site-packages/itsdangerous/_json.py", line 18, in dumps
    return json.dumps(obj, **kwargs)
  File "/usr/local/Cellar/python/3.7.3/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/__init__.py", line 238, in dumps
    **kw).encode(obj)
  File "/usr/local/Cellar/python/3.7.3/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/encoder.py", line 199, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/usr/local/Cellar/python/3.7.3/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/encoder.py", line 257, in iterencode
    return _iterencode(o, 0)
  File "/usr/local/Cellar/python/3.7.3/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/encoder.py", line 179, in default
    raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type bytes is not JSON serializable

However, with the version fixed it seems to work with Python 3.5, just not 3.6 or 3.7. Since internally we use 3.6+ this means we'd have to downgrade (not recommended) or stick with Python 2.7, which is not ideal as support runs out in 2020.

Is this expected after support for Python 3(.5) was added?

bug

Most helpful comment

Just a note in case anyone ran into this issue: if you see an error related to "TypeError: Object of type bytes is not JSON serializable" and you have ever used more than one python versions w/ airflow, clearing cookie or open the airflow webserver in an incognito page of Chrome may fix the problem.

All 12 comments

In fact, I built myself a Docker image with TFX (incl. Airflow, TFMA, GCS, and a few other tools I need) based on Python 3.5. When running the Airflow server and accessing the UI it throws this:

Node: 27b33c83c4bb
-------------------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/src/app/.venv/lib/python3.5/site-packages/flask/app.py", line 1982, in wsgi_app
    response = self.full_dispatch_request()
  File "/usr/src/app/.venv/lib/python3.5/site-packages/flask/app.py", line 1614, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/usr/src/app/.venv/lib/python3.5/site-packages/flask/app.py", line 1517, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/src/app/.venv/lib/python3.5/site-packages/flask/_compat.py", line 33, in reraise
    raise value
  File "/usr/src/app/.venv/lib/python3.5/site-packages/flask/app.py", line 1612, in full_dispatch_request
    rv = self.dispatch_request()
  File "/usr/src/app/.venv/lib/python3.5/site-packages/flask/app.py", line 1598, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/usr/src/app/.venv/lib/python3.5/site-packages/flask_admin/base.py", line 69, in inner
    return self._run_view(f, *args, **kwargs)
  File "/usr/src/app/.venv/lib/python3.5/site-packages/flask_admin/base.py", line 368, in _run_view
    return fn(self, *args, **kwargs)
  File "/usr/src/app/.venv/lib/python3.5/site-packages/flask_login/utils.py", line 258, in decorated_view
    return func(*args, **kwargs)
  File "/usr/src/app/.venv/lib/python3.5/site-packages/airflow/utils/db.py", line 73, in wrapper
    return func(*args, **kwargs)
  File "/usr/src/app/.venv/lib/python3.5/site-packages/airflow/www/views.py", line 2224, in index
    auto_complete_data=auto_complete_data)
  File "/usr/src/app/.venv/lib/python3.5/site-packages/flask_admin/base.py", line 308, in render
    return render_template(template, **kwargs)
  File "/usr/src/app/.venv/lib/python3.5/site-packages/flask/templating.py", line 134, in render_template
    context, ctx.app)
  File "/usr/src/app/.venv/lib/python3.5/site-packages/flask/templating.py", line 116, in _render
    rv = template.render(context)
  File "/usr/src/app/.venv/lib/python3.5/site-packages/jinja2/environment.py", line 1008, in render
    return self.environment.handle_exception(exc_info, True)
  File "/usr/src/app/.venv/lib/python3.5/site-packages/jinja2/environment.py", line 780, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/src/app/.venv/lib/python3.5/site-packages/jinja2/_compat.py", line 37, in reraise
    raise value.with_traceback(tb)
  File "/usr/src/app/.venv/lib/python3.5/site-packages/airflow/www/templates/airflow/dags.html", line 18, in top-level template code
    {% extends "airflow/master.html" %}
  File "/usr/src/app/.venv/lib/python3.5/site-packages/airflow/www/templates/airflow/master.html", line 18, in top-level template code
    {% extends "admin/master.html" %}
  File "/usr/src/app/.venv/lib/python3.5/site-packages/airflow/www/templates/admin/master.html", line 18, in top-level template code
    {% extends 'admin/base.html' %}
  File "/usr/src/app/.venv/lib/python3.5/site-packages/flask_admin/templates/bootstrap3/admin/base.html", line 81, in top-level template code
    {% block tail_js %}
  File "/usr/src/app/.venv/lib/python3.5/site-packages/airflow/www/templates/admin/master.html", line 44, in block "tail_js"
    xhr.setRequestHeader("X-CSRFToken", "{{ csrf_token() }}");
  File "/usr/src/app/.venv/lib/python3.5/site-packages/flask_wtf/csrf.py", line 47, in generate_csrf
    setattr(g, field_name, s.dumps(session[field_name]))
  File "/usr/src/app/.venv/lib/python3.5/site-packages/itsdangerous/serializer.py", line 166, in dumps
    payload = want_bytes(self.dump_payload(obj))
  File "/usr/src/app/.venv/lib/python3.5/site-packages/itsdangerous/url_safe.py", line 42, in dump_payload
    json = super(URLSafeSerializerMixin, self).dump_payload(obj)
  File "/usr/src/app/.venv/lib/python3.5/site-packages/itsdangerous/serializer.py", line 133, in dump_payload
    return want_bytes(self.serializer.dumps(obj, **self.serializer_kwargs))
  File "/usr/src/app/.venv/lib/python3.5/site-packages/itsdangerous/_json.py", line 18, in dumps
    return json.dumps(obj, **kwargs)
  File "/usr/local/lib/python3.5/json/__init__.py", line 237, in dumps
    **kw).encode(obj)
  File "/usr/local/lib/python3.5/json/encoder.py", line 198, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/usr/local/lib/python3.5/json/encoder.py", line 256, in iterencode
    return _iterencode(o, 0)
  File "/usr/local/lib/python3.5/json/encoder.py", line 179, in default
    raise TypeError(repr(o) + " is not JSON serializable")
TypeError: b'5e296790e412a20c89fb531868e8622449aadf64' is not JSON serializable

Previously I ran TFX 0.12 and Python 2.7 fine with a similar Docker image.

Hi @ianhellstrom , the issues was not expected, although previous we only continuous testing using python3.5 so issues were not caught.

I'll investigate more on this issue.

To confirm my understanding, python 2.7 and python3.5 have been working, but Airflow webserver on python 3.6 and 3.7 consistently fails, right?

That is right: 3.6 and 3.7 don't work at all.

I managed to make 3.5 work locally but not inside a Docker image (Ubuntu base with 3.5.7 compiled from source), so that could have been due to missing libraries.

@ianhellstrom Can you share the Dockerfile you used to reproduce the failure?

Also worth mentioning is that in #51 a docker based tutorial is requested. A docker image with necessary dependencies can serve as a good starting point.

It uses our own company-internal base image, so that won't help anyone. I can have a look if I can easily make it generic, but no promises. I can have a look tomorrow.

Here's a minimalistic Python 3.5 (Debian) image that reproduces the problem:

FROM debian

WORKDIR /app

ENV PYTHONPATH=/app \
    VIRTUAL_ENV=/app/.venv \
    PATH=/app/.venv/bin:$PATH

RUN apt update && \
    apt install -y python3.5 python3.5-dev python3.5-venv python3-pip

RUN python3.5 -m pip install --upgrade pip setuptools

RUN rm -rf /var/lib/apt/lists/* && python3.5 -m venv --without-pip /app/.venv

# ---

COPY requirements.txt /app/requirements.txt
COPY entrypoint.sh    /app/entrypoint.sh

ENV AIRFLOW_HOME /app/airflow
ENV SLUGIFY_USES_TEXT_UNIDECODE yes
ENV AIRFLOW__CORE__LOAD_EXAMPLES False

RUN pip install -r requirements.txt
RUN airflow initdb

RUN chmod +x /app/entrypoint.sh

VOLUME /app/tfx/metadata
VOLUME /app/tfx/pipelines
VOLUME /app/tfx/serving

EXPOSE 8080

ENTRYPOINT ["/app/entrypoint.sh"]

The # --- indicates where I would normally split the Dockerfile: one for a Debian + Python3.5 base image and one for TFX add-ons. However, to make it easier I combined the two.

The file requirements.txt has the following contents:

--index-url https://pypi.python.org/simple/

tensorflow==1.13.1
tfx==0.13.0
apache-airflow==1.10.2
docker
backports.ssl_match_hostname

And entrypoint.sh is:

#!/bin/bash
nohup airflow webserver > webserver.log 2>&1 &
airflow scheduler

You can simply run it from the code directory as follows:

docker run \
    -v "$(pwd)"/data:/app/airflow/data \
    -v "$(pwd)"/code/pipelines:/app/airflow/dags \
    -v "$(pwd)"/code/model:/app/airflow/plugins \
    -v "$(pwd)"/.tfx/metadata:/app/tfx/metadata \
    -v "$(pwd)"/.tfx/pipelines:/app/tfx/pipelines \
    -v "$(pwd)"/.tfx/serving:/app/tfx/serving \
    --name "TFX0.13_Airflow1.10.2" \
    -p 8080:8080 \
    -d "$IMAGE_NAME"

It assumes the data, pipelines, and module (model) file live in folders as mounted, which is the setup I have locally. This is different from the TFX repo. It also writes the metadata/pipelines/serving results to a hidden local directory .tfx, so I can access it afterwards with notebooks (even when the container is not running). With .gitignore you can ensure these are not checked in.

This setup also has the benefit that any changes made to the module file or the pipelines themselves are reflected in Airflow (as it runs). So, you can just refresh the DAGs and then re-run to see the effect of changes.

Hi @ianhellstrom, thanks for the dockerfile. I copied this locally but still cannot reproduce: the chicago taxi simple pipeline works okay to run inside a container image built from identical Dockerfile content.

Do I need to upgrade to python3.6 in the image to reproduce?

Huh. That causes the crash log I included on my machine. Do you run the DAG from command line or through the Airflow UI?

I may have to check whether I did something wrong on my computer.

I should have checked the webserver.log file from Airflow earlier: somehow localhost:8080 does not work anymore but 0.0.0.0:8080 does. If I change the URL to that, I can open Airflow and run DAGs for Python 3.5.

@ianhellstrom Thanks. Do you have a reproduce with python 3.6/3.7?

I have not checked it and since it's not officially supported anyway (due to Beam) I do not see an immediate. We can probably close this issue.

Just a note in case anyone ran into this issue: if you see an error related to "TypeError: Object of type bytes is not JSON serializable" and you have ever used more than one python versions w/ airflow, clearing cookie or open the airflow webserver in an incognito page of Chrome may fix the problem.

Was this page helpful?
0 / 5 - 0 ratings