Because Server#run
calls Config#load
within loop.run_until_complete(self.serve(...))
, a config load that involves asynchronously constructing an app fails because the event loop is already running. Note that this config load will fail only if the app name is provided instead of the app itself. This is because in the latter case, the app had already been asynchronously constructed in its own run_until_complete
prior to the run_until_complete
launched by Server
.
pip install -e git+https://github.com/encode/uvicorn#egg=uvicorn
main.py
:import asyncio
async def new_app():
async def app(scope, receive, send):
await send({
'type': 'http.response.start',
'status': 200,
'headers': [
[b'content-type', b'text/plain'],
],
})
await send({
'type': 'http.response.body',
'body': b'Hello, world!',
})
return app
app = asyncio.get_event_loop().run_until_complete(new_app())
uvicorn main:app
.INFO: Started server process [xxx]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
And that a server request would return Hello, world!
Traceback (most recent call last):
File "/home/mishac/dev/python/personal/async_init/venv/bin/uvicorn", line 33, in <module>
sys.exit(load_entry_point('uvicorn', 'console_scripts', 'uvicorn')())
File "/home/mishac/dev/python/personal/async_init/venv/lib/python3.8/site-packages/click/core.py", line 829, in __call__
return self.main(*args, **kwargs)
File "/home/mishac/dev/python/personal/async_init/venv/lib/python3.8/site-packages/click/core.py", line 782, in main
rv = self.invoke(ctx)
File "/home/mishac/dev/python/personal/async_init/venv/lib/python3.8/site-packages/click/core.py", line 1066, in invoke
return ctx.invoke(self.callback, **ctx.params)
File "/home/mishac/dev/python/personal/async_init/venv/lib/python3.8/site-packages/click/core.py", line 610, in invoke
return callback(*args, **kwargs)
File "/home/mishac/dev/python/personal/async_init/venv/src/uvicorn/uvicorn/main.py", line 362, in main
run(**kwargs)
File "/home/mishac/dev/python/personal/async_init/venv/src/uvicorn/uvicorn/main.py", line 386, in run
server.run()
File "/home/mishac/dev/python/personal/async_init/venv/src/uvicorn/uvicorn/server.py", line 49, in run
loop.run_until_complete(self.serve(sockets=sockets))
File "uvloop/loop.pyx", line 1456, in uvloop.loop.Loop.run_until_complete
File "/home/mishac/dev/python/personal/async_init/venv/src/uvicorn/uvicorn/server.py", line 56, in serve
config.load()
File "/home/mishac/dev/python/personal/async_init/venv/src/uvicorn/uvicorn/config.py", line 308, in load
self.loaded_app = import_from_string(self.app)
File "/home/mishac/dev/python/personal/async_init/venv/src/uvicorn/uvicorn/importer.py", line 20, in import_from_string
module = importlib.import_module(module_str)
File "/usr/lib/python3.8/importlib/__init__.py", line 127, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "<frozen importlib._bootstrap>", line 1014, in _gcd_import
File "<frozen importlib._bootstrap>", line 991, in _find_and_load
File "<frozen importlib._bootstrap>", line 975, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 671, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 783, in exec_module
File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
File "./main.py", line 19, in <module>
app = asyncio.get_event_loop().run_until_complete(new_app())
File "uvloop/loop.pyx", line 1450, in uvloop.loop.Loop.run_until_complete
File "uvloop/loop.pyx", line 1443, in uvloop.loop.Loop.run_until_complete
File "uvloop/loop.pyx", line 1351, in uvloop.loop.Loop.run_forever
File "uvloop/loop.pyx", line 480, in uvloop.loop.Loop._run
RuntimeError: this event loop is already running.
Event loop started running before app could be asynchronously constructed.
Ubuntu 20.04
CPython 3.8.5
Uvicorn 0.13.3
Replacing step 1 with pip install -e git+https://github.com/MarioIshac/uvicorn@feat-async-construction#egg=uvicorn
will achieve the expected behavior.
The particular benefit I get from having the app asynchronously & eagerly constructed instead of lazily constructed within async def app(...)
is that I can take advantage of gunicorn preloading. I can also have the server fail-fast instead of on-first-request in the case that the asynchronous construction fails (for example, database connection pool could not be created).
Pay now to fund the work behind this issue.
Get updates on progress being made.
Maintainer is rewarded once the issue is completed.
You're funding impactful open source efforts
You want to contribute to this effort
You want to get funding like this too