14
14
from reactpy .utils import vdom_to_html
15
15
16
16
if TYPE_CHECKING :
17
+ import uvicorn
17
18
from asgiref .typing import ASGIApplication
18
19
19
20
PATH_PREFIX = PurePosixPath ("/_reactpy" )
20
21
MODULES_PATH = PATH_PREFIX / "modules"
21
22
ASSETS_PATH = PATH_PREFIX / "assets"
22
23
STREAM_PATH = PATH_PREFIX / "stream"
23
-
24
24
CLIENT_BUILD_DIR = Path (_reactpy_file_path ).parent / "_static" / "app" / "dist"
25
25
26
- try :
26
+
27
+ async def serve_with_uvicorn (
28
+ app : ASGIApplication | Any ,
29
+ host : str ,
30
+ port : int ,
31
+ started : asyncio .Event | None ,
32
+ ) -> None :
33
+ """Run a development server for an ASGI application"""
27
34
import uvicorn
28
- except ImportError : # nocov
29
- pass
30
- else :
31
-
32
- async def serve_development_asgi (
33
- app : ASGIApplication | Any ,
34
- host : str ,
35
- port : int ,
36
- started : asyncio .Event | None ,
37
- ) -> None :
38
- """Run a development server for an ASGI application"""
39
- server = uvicorn .Server (
40
- uvicorn .Config (
41
- app ,
42
- host = host ,
43
- port = port ,
44
- loop = "asyncio" ,
45
- reload = True ,
46
- )
35
+
36
+ server = uvicorn .Server (
37
+ uvicorn .Config (
38
+ app ,
39
+ host = host ,
40
+ port = port ,
41
+ loop = "asyncio" ,
47
42
)
48
- server .config .setup_event_loop ()
49
- coros : list [Awaitable [Any ]] = [server .serve ()]
43
+ )
44
+ server .config .setup_event_loop ()
45
+ coros : list [Awaitable [Any ]] = [server .serve ()]
50
46
51
- # If a started event is provided, then use it signal based on `server.started`
52
- if started :
53
- coros .append (_check_if_started (server , started ))
47
+ # If a started event is provided, then use it signal based on `server.started`
48
+ if started :
49
+ coros .append (_check_if_started (server , started ))
54
50
55
- try :
56
- await asyncio .gather (* coros )
57
- finally :
58
- # Since we aren't using the uvicorn's `run()` API, we can't guarantee uvicorn's
59
- # order of operations. So we need to make sure `shutdown()` always has an initialized
60
- # list of `self.servers` to use.
61
- if not hasattr (server , "servers" ): # nocov
62
- server .servers = []
63
- await asyncio .wait_for (server .shutdown (), timeout = 3 )
51
+ try :
52
+ await asyncio .gather (* coros )
53
+ finally :
54
+ # Since we aren't using the uvicorn's `run()` API, we can't guarantee uvicorn's
55
+ # order of operations. So we need to make sure `shutdown()` always has an initialized
56
+ # list of `self.servers` to use.
57
+ if not hasattr (server , "servers" ): # nocov
58
+ server .servers = []
59
+ await asyncio .wait_for (server .shutdown (), timeout = 3 )
64
60
65
61
66
62
async def _check_if_started (server : uvicorn .Server , started : asyncio .Event ) -> None :
@@ -72,8 +68,7 @@ async def _check_if_started(server: uvicorn.Server, started: asyncio.Event) -> N
72
68
def safe_client_build_dir_path (path : str ) -> Path :
73
69
"""Prevent path traversal out of :data:`CLIENT_BUILD_DIR`"""
74
70
return traversal_safe_path (
75
- CLIENT_BUILD_DIR ,
76
- * ("index.html" if path in ("" , "/" ) else path ).split ("/" ),
71
+ CLIENT_BUILD_DIR , * ("index.html" if path in {"" , "/" } else path ).split ("/" )
77
72
)
78
73
79
74
@@ -140,6 +135,9 @@ class CommonOptions:
140
135
url_prefix : str = ""
141
136
"""The URL prefix where ReactPy resources will be served from"""
142
137
138
+ serve_index_route : bool = True
139
+ """Automatically generate and serve the index route (``/``)"""
140
+
143
141
def __post_init__ (self ) -> None :
144
142
if self .url_prefix and not self .url_prefix .startswith ("/" ):
145
143
msg = "Expected 'url_prefix' to start with '/'"
0 commit comments