uvicorn icon indicating copy to clipboard operation
uvicorn copied to clipboard

Move `Config#load` out of `Server#serve` to enable Eager Asynchronous Construction of App

Open MarioIshac opened this issue 4 years ago • 6 comments

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 avatar Jan 22 '21 02:01 MarioIshac

@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 avatar Jan 22 '21 15:01 florimondmanca

@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.

MarioIshac avatar Jan 22 '21 15:01 MarioIshac

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

euri10 avatar Jan 23 '21 17:01 euri10

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.

MarioIshac avatar Jan 23 '21 20:01 MarioIshac

I have kind of the feeling this could be resurrected for 3.10

euri10 avatar Jun 07 '21 13:06 euri10

@euri10 I don't think this makes sense anymore, given asyncio.get_event_loop().run_until_complete(...) is no longer supported in python3.10+

graingert avatar Aug 13 '21 12:08 graingert