In my flask app, the shell appears to be using different code than the routes. How is this possible? I was having DNS resolution issues with flask-mail and docker, so I ran the same code in both the shell and a route. The shell ran without issue, but the route returned an error that appears to be related to this github issue. When monkey patching is on the shell errors and works fine when it doesn’t. The route errors no matter what.
I am having other issues with differing behavior in the shell and routes regarding container name references, but it’s weird to see differing behavior between the shell and routes in another aspect of the app.
flask shell
>>> import dns.resolver
>>> answers = dns.resolver.resolve('google.com', 'A')
>>> answers.canonical_name
route
@production.route('/test_dns', methods=['GET'])
@login_required
def test_dns():
import dns.resolver
answers = dns.resolver.resolve('google.com', 'A')
print(f'{answers.canonical_name}')
return ''
route error
web_1 | [2024-08-01 16:28:16 +0000] [10] [ERROR] Error handling request /production/test_dns
web_1 | Traceback (most recent call last):
web_1 | File "/usr/local/lib/python3.12/site-packages/gunicorn/workers/base_async.py", line 55, in handle
web_1 | self.handle_request(listener_name, req, client, addr)
web_1 | File "/usr/local/lib/python3.12/site-packages/gunicorn/workers/base_async.py", line 108, in handle_request
web_1 | respiter = self.wsgi(environ, resp.start_response)
web_1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
web_1 | File "/usr/local/lib/python3.12/site-packages/flask/app.py", line 1478, in __call__
web_1 | return self.wsgi_app(environ, start_response)
web_1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
web_1 | File "/usr/local/lib/python3.12/site-packages/flask/app.py", line 1458, in wsgi_app
web_1 | response = self.handle_exception(e)
web_1 | ^^^^^^^^^^^^^^^^^^^^^^^^
web_1 | File "/usr/local/lib/python3.12/site-packages/flask/app.py", line 1455, in wsgi_app
web_1 | response = self.full_dispatch_request()
web_1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
web_1 | File "/usr/local/lib/python3.12/site-packages/flask/app.py", line 869, in full_dispatch_request
web_1 | rv = self.handle_user_exception(e)
web_1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
web_1 | File "/usr/local/lib/python3.12/site-packages/flask/app.py", line 867, in full_dispatch_request
web_1 | rv = self.dispatch_request()
web_1 | ^^^^^^^^^^^^^^^^^^^^^^^
web_1 | File "/usr/local/lib/python3.12/site-packages/flask/app.py", line 852, in dispatch_request
web_1 | return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
web_1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
web_1 | File "/usr/local/lib/python3.12/site-packages/flask_login/utils.py", line 290, in decorated_view
web_1 | return current_app.ensure_sync(func)(*args, **kwargs)
web_1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
web_1 | File "/app/web/production/views.py", line 67, in test_dns
web_1 | answers = dns.resolver.resolve('google.com', 'A')
web_1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
web_1 | File "/usr/local/lib/python3.12/site-packages/dns/resolver.py", line 1565, in resolve
web_1 | return get_default_resolver().resolve(
web_1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
web_1 | File "/usr/local/lib/python3.12/site-packages/dns/resolver.py", line 1321, in resolve
web_1 | timeout = self._compute_timeout(start, lifetime, resolution.errors)
web_1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
web_1 | File "/usr/local/lib/python3.12/site-packages/dns/resolver.py", line 1075, in _compute_timeout
web_1 | raise LifetimeTimeout(timeout=duration, errors=errors)
web_1 | dns.resolver.LifetimeTimeout: The resolution lifetime expired after 5.106 seconds: Server Do53:127.0.0.11@53 answered udp() got an unexpected keyword argument 'ignore_errors'; Server Do53:127.0.0.11@53 answered udp() got an unexpected keyword argument 'ignore_errors'; Server Do53:127.0.0.11@53 answered udp() got an unexpected keyword argument 'ignore_errors'; Server Do53:127.0.0.11@53 answered udp() got an unexpected keyword argument 'ignore_errors'; Server Do53:127.0.0.11@53 answered udp() got an unexpected keyword argument 'ignore_errors'; Server Do53:127.0.0.11@53 answered udp() got an unexpected keyword argument 'ignore_errors'
Minimum Reproducible Example
.
├── docker/
│ └── Dockerfile
├── app.py
├── docker-compose.yml
└── requirements.txt
docker-compose.yml
services:
web:
build:
context: .
dockerfile: ./docker/Dockerfile
command: gunicorn --worker-class eventlet --timeout 120 -w 6 -b 0.0.0.0:5000 app:app
# command: flask run --host=0.0.0.0 --port=5000
volumes:
- .:/app
ports:
- '5000:5000'
environment:
- FLASK_APP=app
requirements.txt
eventlet==0.34.2
Flask==3.0.0
gunicorn==21.2.0
docker/Dockerfile/
FROM python:3.12.4-bookworm
# making python prints go to stdout
ENV PYTHONUNBUFFERED 1
# don't write .pyc files
ENV PYTHONDONTWRITEBYTECODE 1
RUN apt-get update
&& apt-get install -y build-essential
&& apt-get install -y gunicorn python3-gunicorn
&& apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false
&& rm -rf /var/lib/apt/lists/*
COPY ./requirements.txt /requirements.txt
RUN pip install -r /requirements.txt
WORKDIR /app
app.py
from flask import Flask
app = Flask(__name__, static_folder='app', static_url_path="/app")
@app.route("/test_dns")
def test_dns():
import dns.resolver
answers = dns.resolver.resolve('google.com', 'A')
print(f'{answers.canonical_name}')
return ''
In this minimal example the route only errors when command: gunicorn –worker-class eventlet –timeout 120 -w 6 -b 0.0.0.0:5000 app:app is used instead of command: flask run --host=0.0.0.0 --port=5000
.
Error:
web_1 | [2024-08-02 18:00:34 +0000] [1] [INFO] Listening at: http://0.0.0.0:5000 (1)
web_1 | [2024-08-02 18:00:34 +0000] [1] [INFO] Using worker: eventlet
web_1 | [2024-08-02 18:00:34 +0000] [7] [INFO] Booting worker with pid: 7
web_1 | [2024-08-02 18:00:34 +0000] [8] [INFO] Booting worker with pid: 8
web_1 | [2024-08-02 18:00:34 +0000] [9] [INFO] Booting worker with pid: 9
web_1 | [2024-08-02 18:00:34 +0000] [10] [INFO] Booting worker with pid: 10
web_1 | [2024-08-02 18:00:34 +0000] [11] [INFO] Booting worker with pid: 11
web_1 | [2024-08-02 18:00:34 +0000] [12] [INFO] Booting worker with pid: 12
web_1 | [2024-08-02 18:01:11,183] ERROR in app: Exception on /test_dns [GET]
web_1 | Traceback (most recent call last):
web_1 | File "/usr/local/lib/python3.12/site-packages/flask/app.py", line 1455, in wsgi_app
web_1 | response = self.full_dispatch_request()
web_1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
web_1 | File "/usr/local/lib/python3.12/site-packages/flask/app.py", line 869, in full_dispatch_request
web_1 | rv = self.handle_user_exception(e)
web_1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
web_1 | File "/usr/local/lib/python3.12/site-packages/flask/app.py", line 867, in full_dispatch_request
web_1 | rv = self.dispatch_request()
web_1 | ^^^^^^^^^^^^^^^^^^^^^^^
web_1 | File "/usr/local/lib/python3.12/site-packages/flask/app.py", line 852, in dispatch_request
web_1 | return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
web_1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
web_1 | File "/app/app.py", line 9, in test_dns
web_1 | answers = dns.resolver.resolve('google.com', 'A')
web_1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
web_1 | File "/usr/local/lib/python3.12/site-packages/dns/resolver.py", line 1565, in resolve
web_1 | return get_default_resolver().resolve(
web_1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
web_1 | File "/usr/local/lib/python3.12/site-packages/dns/resolver.py", line 1321, in resolve
web_1 | timeout = self._compute_timeout(start, lifetime, resolution.errors)
web_1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
web_1 | File "/usr/local/lib/python3.12/site-packages/dns/resolver.py", line 1075, in _compute_timeout
web_1 | raise LifetimeTimeout(timeout=duration, errors=errors)
web_1 | dns.resolver.LifetimeTimeout: The resolution lifetime expired after 5.106 seconds: Server Do53:127.0.0.11@53 answered udp() got an unexpected keyword argument 'ignore_errors'; Server Do53:127.0.0.11@53 answered udp() got an unexpected keyword argument 'ignore_errors'; Server Do53:127.0.0.11@53 answered udp() got an unexpected keyword argument 'ignore_errors'; Server Do53:127.0.0.11@53 answered udp() got an unexpected keyword argument 'ignore_errors'; Server Do53:127.0.0.11@53 answered udp() got an unexpected keyword argument 'ignore_errors'; Server Do53:127.0.0.11@53 answered udp() got an unexpected keyword argument 'ignore_errors'
pip freeze inside container
blinker==1.8.2
click==8.1.7
dnspython==2.6.1
eventlet==0.34.2
Flask==3.0.0
greenlet==3.0.3
gunicorn==21.2.0
itsdangerous==2.2.0
Jinja2==3.1.4
MarkupSafe==2.1.5
packaging==24.1
setuptools==71.1.0
six==1.16.0
Werkzeug==3.0.3
wheel==0.43.0
2
Answers
Solved by updating python modules in requirements.txt
Glad you’ve solved your problem. For the sake of posterity, it looks like you were hitting this issue, which was opened back in Feburary 2024.
This is a result of this change in
dnspython
.The fix was provided in this pull request, and is available in
eventlet
release 0.35.2.