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 uvicorn
s (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
54
and55
insubprocess.py
# if stdin_fileno is not None:
# sys.stdin = os.fdopen(stdin_fileno)
- Setting
'stdin_fileno': None
on line35
insubprocess.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 GitHub Comments
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,
reload
uses multiprocess spawn under the hood so you’re essentially multiprocessing^2