pythondockerflaskflask-socketioeventlet

eventlet throws error on import in docker


I have been having some odd issues with docker today. I described one issue @ pathlib: cannot import name 'Sequence' from 'collections'. I didn't really need one of the packages that was causing the break so I took it out. Note that this issue was only happening in docker.

After taking out artifactory package dependency install on docker passed successfully, but am hitting TypeError in my flask app init file when importing: from flask_socketio import SocketIO, emit which requires eventlet which is where the error comes from:

web_1  |     from eventlet import greenio
web_1  |   File "/usr/local/lib/python3.10/site-packages/eventlet/greenio/__init__.py", line 3, in <module>
web_1  |     from eventlet.greenio.base import *  # noqa
web_1  |   File "/usr/local/lib/python3.10/site-packages/eventlet/greenio/base.py", line 32, in <module>
web_1  |     socket_timeout = eventlet.timeout.wrap_is_timeout(socket.timeout)
web_1  |   File "/usr/local/lib/python3.10/site-packages/eventlet/timeout.py", line 166, in wrap_is_timeout
web_1  |     base.is_timeout = property(lambda _: True)
web_1  | TypeError: cannot set 'is_timeout' attribute of immutable type 'TimeoutError'
web_1  | ]

requirements.txt:

alembic==1.7.3
aniso8601==8.0.0
appdirs==1.4.4
attrs==20.3.0
bcrypt==3.2.0
beautifulsoup4==4.9.3
bidict==0.21.3
blinker==1.4
boto3==1.18.50
botocore==1.21.50
bs4==0.0.1
cachelib==0.3.0
certifi==2020.12.5
cffi==1.14.5
chardet==3.0.4
click==8.0.1
cryptography==3.4.6
distlib==0.3.2
dnspython==1.16.0
dominate==2.6.0
email-validator==1.1.3
et-xmlfile==1.1.0
eventlet==0.30.2
filelock==3.0.12
Flask==2.0.1
Flask-Bootstrap==3.3.7.1
Flask-Login==0.5.0
Flask-Mail==0.9.1
flask-marshmallow==0.14.0
Flask-Migrate==3.1.0
Flask-RESTful==0.3.8
Flask-Session==0.4.0
Flask-SocketIO==5.1.1
Flask-SQLAlchemy==2.5.1
Flask-User==1.0.2.2
Flask-WTF==0.15.1
greenlet==1.1.0
gunicorn==20.1.0
idna==2.10
iniconfig==1.1.1
is-safe-url==1.0
itsdangerous==2.0.1
Jinja2==3.0.1
jmespath==0.10.0
Mako==1.1.5
MarkupSafe==2.0.1
marshmallow==3.12.2
marshmallow-sqlalchemy==0.26.1
openpyxl==3.0.7
packaging==20.9
paramiko==2.7.2
passlib==1.7.4
pexpect==4.8.0
pluggy==0.13.1
psycopg2-binary==2.9.1
ptyprocess==0.7.0
py==1.10.0
pycparser==2.20
PyNaCl==1.4.0
pyparsing==2.4.7
pytest==6.2.3
python-dateutil==2.8.1
python-dotenv==0.19.0
python-engineio==4.2.1
python-socketio==5.4.0
pytz==2021.1
requests==2.24.0
s3transfer==0.5.0
scp==0.13.3
shippo==2.0.2
simplejson==3.17.2
six==1.15.0
soupsieve==2.2
SQLAlchemy==1.4.15
SQLAlchemy-Utils==0.37.8
toml==0.10.2
urllib3==1.25.11
virtualenv==20.4.7
visitor==0.1.3
Werkzeug==2.0.1
WTForms==2.3.3
XlsxWriter==1.4.3

So when run with docker it fails with the above error. My entrypoint looks like:

flask db init
flask db migrate
flask db upgrade
gunicorn "main:create_app()" --workers 1 --threads 10 --bind=0.0.0.0:5010 --worker-class eventlet

But if the same gunicorn command is run locally the app boots with no issues.

UPDATE: I wanted to try a more reproducible example and have narrowed it down as such: app.py:

from flask import Flask
import flask_socketio

app = Flask(__name__)

def create_app():
    return app

requirements.txt:

bidict==0.21.3
click==8.0.1
dnspython==2.1.0
eventlet==0.32.0
Flask==2.0.2
Flask-SocketIO==5.1.1
greenlet==1.1.2
gunicorn==20.1.0
itsdangerous==2.0.1
Jinja2==3.0.2
MarkupSafe==2.0.1
python-engineio==4.2.1
python-socketio==5.4.0
six==1.16.0
Werkzeug==2.0.2

Dockerfile:

FROM python:3

WORKDIR /usr/src/app

COPY . .

RUN pip install --no-cache-dir -r requirements.txt

EXPOSE 8000

CMD ["gunicorn", "-b", "0.0.0.0:8000", "app:create_app()", "--worker-class", "eventlet"]

Executed with docker build/docker run -it --publish 8888:8000 image_name.

and get the same error. If eventlet is removed from requirements, there is no error.

Am I doing something wrong? I just want to run flask-socketio with eventlet


Solution

  • Searching for the exception, leads to the corresponding eventlet issue: https://github.com/eventlet/eventlet/issues/687

    The summary is that eventlet (0.32.0) is currently not compatible with Python 3.10 because it tries to patch types that have become immutable in Python 3.10.

    Like with your requirements, it is good practice to be more specific with your Docker dependencies too. Today using the tag 3 for the Python Docker image will give you 3.10.0, unless it is using a cache. In the future it could be a different version. Since there is a compatibility issue with Python 3.10, use Python 3.9 - the currently latest Python 3.9 Docker tag is 3.9.7.

    i.e. it should work once you change your first line of the Dockerfile to:

    FROM python:3.9.7