uvicorn.run with reload or workers in ProcessPoolExecutor fails with Bad file descriptor error
See original GitHub issueGreetings!
I have run into an issue when trying to spawn several local uvicorns (obviously bound to different ports) from within Python. Instead of launching each one manually in a separate terminal, I have set up a function that spawns the desired servers in a ProcessPoolExecutor (imported from concurrent.futures). However, if the servers are configured with the workers or reload options, I get the following error:
Process SpawnProcess-1:1:
Traceback (most recent call last):
File "/usr/lib/python3.8/multiprocessing/process.py", line 315, in _bootstrap
self.run()
File "/usr/lib/python3.8/multiprocessing/process.py", line 108, in run
self._target(*self._args, **self._kwargs)
File "/home/cantor/code/envs/tabula/lib/python3.8/site-packages/uvicorn/subprocess.py", line 55, in subprocess_started
sys.stdin = os.fdopen(stdin_fileno)
File "/usr/lib/python3.8/os.py", line 1023, in fdopen
return io.open(fd, *args, **kwargs)
OSError: [Errno 9] Bad file descriptor
Stating the obvious, there is an issue with reopening stdin in the child process.
Simple workarounds resulting in happy server spawning:
- Commenting out lines
54and55insubprocess.py
# if stdin_fileno is not None:
# sys.stdin = os.fdopen(stdin_fileno)
- Setting
'stdin_fileno': Noneon line35insubprocess.py
To reproduce
pip install fastapi uvicorn
(Assuming the code is located in launch.py)
With workers:
import uvicorn
from fastapi import FastAPI
from concurrent.futures import ProcessPoolExecutor
app = FastAPI()
if __name__ == '__main__':
with ProcessPoolExecutor() as executor:
executor.submit(uvicorn.run, 'launch:app', workers = 3)
Error:
Process SpawnProcess-1:3:
Traceback (most recent call last):
File "/usr/lib/python3.8/multiprocessing/process.py", line 315, in _bootstrap
self.run()
File "/usr/lib/python3.8/multiprocessing/process.py", line 108, in run
self._target(*self._args, **self._kwargs)
File "/usr/lib/python3.8/site-packages/uvicorn/subprocess.py", line 55, in subprocess_started
sys.stdin = os.fdopen(stdin_fileno)
File "/usr/lib/python3.8/os.py", line 1023, in fdopen
return io.open(fd, *args, **kwargs)
OSError: [Errno 9] Bad file descriptor
Process SpawnProcess-1:1:
Traceback (most recent call last):
File "/usr/lib/python3.8/multiprocessing/process.py", line 315, in _bootstrap
self.run()
File "/usr/lib/python3.8/multiprocessing/process.py", line 108, in run
self._target(*self._args, **self._kwargs)
File "/usr/lib/python3.8/site-packages/uvicorn/subprocess.py", line 55, in subprocess_started
sys.stdin = os.fdopen(stdin_fileno)
File "/usr/lib/python3.8/os.py", line 1023, in fdopen
return io.open(fd, *args, **kwargs)
OSError: [Errno 9] Bad file descriptor
Process SpawnProcess-1:2:
Traceback (most recent call last):
File "/usr/lib/python3.8/multiprocessing/process.py", line 315, in _bootstrap
self.run()
File "/usr/lib/python3.8/multiprocessing/process.py", line 108, in run
self._target(*self._args, **self._kwargs)
File "/usr/lib/python3.8/site-packages/uvicorn/subprocess.py", line 55, in subprocess_started
sys.stdin = os.fdopen(stdin_fileno)
File "/usr/lib/python3.8/os.py", line 1023, in fdopen
return io.open(fd, *args, **kwargs)
OSError: [Errno 9] Bad file descriptor
With reload:
import uvicorn
from fastapi import FastAPI
from concurrent.futures import ProcessPoolExecutor
app = FastAPI()
if __name__ == '__main__':
with ProcessPoolExecutor() as executor:
executor.submit(uvicorn.run, 'launch:app', reload = True)
Error:
Process SpawnProcess-1:1:
Traceback (most recent call last):
File "/usr/lib/python3.8/multiprocessing/process.py", line 315, in _bootstrap
self.run()
File "/usr/lib/python3.8/multiprocessing/process.py", line 108, in run
self._target(*self._args, **self._kwargs)
File "/usr/lib/python3.8/site-packages/uvicorn/subprocess.py", line 55, in subprocess_started
sys.stdin = os.fdopen(stdin_fileno)
File "/usr/lib/python3.8/os.py", line 1023, in fdopen
return io.open(fd, *args, **kwargs)
OSError: [Errno 9] Bad file descriptor
With 'stdin_fileno': None on line 35 in subprocess.py:
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [705451] using statreload
INFO: Started server process [705462]
INFO: Waiting for application startup.
INFO: Application startup complete.
Expected behavior
uvicorn server starting normally with a reloader or one or more workers.
Actual behavior
Server runs, but no reloader or worker subprocesses.
Environment
- OS: Ubuntu 20.04
- Python: CPython 3.8.6
- Uvicorn version: 0.12.3
Issue Analytics
- State:
- Created 3 years ago
- Comments:9 (5 by maintainers)
Top Related StackOverflow Question
ping @blueyed, maybe you have insights on this !
the original commit seems specifically tied to pdb, which I never use and have very little experience on
on top of that it was written before we changed the default multiprocessing context to be the spawn context which is not the linux default, so maybe the interaction is different than with a plain forked process,
I’d say @likeanaxon send a PR on this the way you think it should work, then we’ll advise !
the use case wont likely be supported,
reloaduses multiprocess spawn under the hood so you’re essentially multiprocessing^2