uvicorn
uvicorn copied to clipboard
Move `Config#load` out of `Server#serve` to enable Eager Asynchronous Construction of App
Fixes #941 by identifying the 3 call sites (test utility, gunicorn worker, uvicorn runner itself) Server#serve
is used at and moving Config#load
to those sites.
@MarioIshac Thanks for this PR. I think this makes sense, but just need a bit of careful thinking.
How does this behave with Uvicorn multiprocessing? Eg --workers 3
, and --reload
? Do we need to change anything else there?
Edit: okay, answering my own question, it seems we're all good. Here's how we spawn for each case (normal, reload, multiple workers):
https://github.com/encode/uvicorn/blob/c6b80fce49e9ad53f514262f4c1eb7f9a6798620/uvicorn/main.py#L377-L386
In each case we target server.run()
, so the scope of when we call config.load()
just gets moved from server.serve()
to server.run()
.
@florimondmanca I'll recheck the interaction between this PR and uvicorn multiprocessing to make sure I didn't miss anything. I also will take a look at how this PR behaves with apps imported as the object itself (uvicorn.run(main.app
) instead of by name (uvicorn.run("main:app")
), since after our discussion I realized that this might affect the PR. I've got to go to work in a bit, but I'll have an update later today.
I think this is looking good.
@euri10, any opinion by chance?
@MarioIshac Would it be possible to add a test for this, eg by passing an app factory (see associated existing tests) that runs something on the event loop?
yep, a test for that is a good idea
I am working on writing the test, but it looks like @pytest.mark.asyncio
already runs the test function in its event loop, so constructing the app within the test function will fail there. But some test functions (such as within test_lifespan.py
) are managing the event loop themselves, so I'll see if I can do something like that.
I have kind of the feeling this could be resurrected for 3.10
@euri10 I don't think this makes sense anymore, given asyncio.get_event_loop().run_until_complete(...)
is no longer supported in python3.10+