gunicorn
gunicorn copied to clipboard
module invocation breaks USR2 upgrading, as modified argv results in modified path
Mixing sys.executable and parsed argv produces unintentional sys.path changes on re-exec:
# python -m gunicorn my:app
Booting worker with pid: 201117
[..]
# kill -USR2 $(pgrep --oldest -f "gunicorn")
Handling signal: usr2
[..]
File "/venv/lib/python3.11/site-packages/werkzeug/serving.py", line 28, in <module>
from http.server import BaseHTTPRequestHandler
ModuleNotFoundError: No module named 'http.server'
- this is not an issue when you run
gunicorn my:app
(withargv[0] == '[..]/gunicorn'
) - this is an issue when
argv[0]
is significant, such aspython3 -m gunicorn my:app
- then parsed sys.argv may point to
[..]/__main__.py
- then relaunching using parsed argv will result in
sys.path
listing directory containing__main__.py
- then wsgi apps using
werkzeug
(such asflask
) will crash because we clobbered thehttp
name - which we never meant to do anyway
- and crucially, did not do .. until we launched new master after USR2
- then parsed sys.argv may point to
https://github.com/benoitc/gunicorn/blob/903792f152af6a27033d458020923cb2bcb11459/gunicorn/arbiter.py#L68-L76
(Side note: The p
is wrong and scaring static analyzers, but harmless in cPython. I suggest cleaning it up while we are at it.)
https://github.com/benoitc/gunicorn/blob/903792f152af6a27033d458020923cb2bcb11459/gunicorn/arbiter.py#L430-L431
I think we just did not have the option to use the original arguments (added in 3.10):
diff a/gunicorn/arbiter.py b/gunicorn/arbiter.py
- os.execve(sys.executable, [sys.executable, *sys.argv[:]], environ)
+ os.execve(sys.executable, sys.orig_argv, environ)
Objections against just doing just that, on Python versions where we can? Even if we cannot do that for some reason, I would still add a warning when launching with clearly unintended sys.path
- it is a common source of confusion.