66from pathlib import Path , PurePosixPath
77from typing import Any , Awaitable , Sequence , cast
88
9+ import uvicorn
910from asgiref .typing import ASGIApplication
10- from uvicorn .config import Config as UvicornConfig
11- from uvicorn .server import Server as UvicornServer
1211
1312from reactpy import __file__ as _reactpy_file_path
1413from reactpy import html
@@ -31,29 +30,35 @@ async def serve_development_asgi(
3130 port : int ,
3231 started : asyncio .Event | None ,
3332) -> None :
34- """Run a development server for starlette """
35- server = UvicornServer (
36- UvicornConfig (
33+ """Run a development server for an ASGI application """
34+ server = uvicorn . Server (
35+ uvicorn . Config (
3736 app ,
3837 host = host ,
3938 port = port ,
4039 loop = "asyncio" ,
4140 reload = True ,
4241 )
4342 )
44-
43+ server . config . setup_event_loop ()
4544 coros : list [Awaitable [Any ]] = [server .serve ()]
4645
46+ # If a started event is provided, then use it signal based on `server.started`
4747 if started :
4848 coros .append (_check_if_started (server , started ))
4949
5050 try :
5151 await asyncio .gather (* coros )
5252 finally :
53+ # Since we aren't using the uvicorn's `run()` API, we can't guarantee uvicorn's
54+ # order of operations. So we need to make sure `shutdown()` always has an initialized
55+ # list of `self.servers` to use.
56+ if not hasattr (server , "servers" ): # pragma: no cover
57+ server .servers = []
5358 await asyncio .wait_for (server .shutdown (), timeout = 3 )
5459
5560
56- async def _check_if_started (server : UvicornServer , started : asyncio .Event ) -> None :
61+ async def _check_if_started (server : uvicorn . Server , started : asyncio .Event ) -> None :
5762 while not server .started :
5863 await asyncio .sleep (0.2 )
5964 started .set ()
0 commit comments