66import urllib .parse
77from collections .abc import Sequence
88from pathlib import Path
9+ from typing import Coroutine
910
1011import aiofiles
1112import orjson
1718 vdom_head_elements_to_html ,
1819)
1920from reactpy .backend .hooks import ConnectionContext
20- from reactpy .backend .mimetypes import DEFAULT_MIME_TYPES
21+ from reactpy .backend .mimetypes import MIME_TYPES
2122from reactpy .backend .types import Connection , Location
2223from reactpy .config import REACTPY_WEB_MODULES_DIR
2324from reactpy .core .layout import Layout
2425from reactpy .core .serve import serve_layout
25- from reactpy .core .types import VdomDict
26+ from reactpy .core .types import ComponentConstructor , VdomDict
2627
2728DEFAULT_STATIC_PATH = f"{ os .getcwd ()} /static"
2829DEFAULT_BLOCK_SIZE = 8192
3233class ReactPy :
3334 def __init__ (
3435 self ,
35- application = None ,
36+ app_or_component : ComponentConstructor | Coroutine ,
37+ * ,
3638 dispatcher_path : str = "^reactpy/stream/([^/]+)/?" ,
3739 js_modules_path : str | None = "^reactpy/modules/([^/]+)/?" ,
3840 static_path : str | None = "^reactpy/static/([^/]+)/?" ,
3941 static_dir : str | None = DEFAULT_STATIC_PATH ,
4042 head : Sequence [VdomDict ] | VdomDict | str = "" ,
4143 ) -> None :
42- self .user_app = guarantee_single_callable (application )
44+ self .component = (
45+ app_or_component
46+ if isinstance (app_or_component , ComponentConstructor )
47+ else None
48+ )
49+ self .user_app = (
50+ guarantee_single_callable (app_or_component )
51+ if not self .component and asyncio .iscoroutinefunction (app_or_component )
52+ else None
53+ )
54+ if not self .component and not self .user_app :
55+ raise TypeError (
56+ "The first argument to `ReactPy` must be a component or an ASGI application."
57+ )
4358 self .dispatch_path = re .compile (dispatcher_path )
4459 self .js_modules_path = re .compile (js_modules_path )
4560 self .static_path = re .compile (static_path )
@@ -66,32 +81,35 @@ async def __call__(self, scope, receive, send) -> None:
6681 return
6782
6883 # User tried to use an unsupported HTTP method
69- if scope ["method" ] not in ("GET" , "HEAD" ):
84+ if scope ["type" ] == "http" and scope [ " method" ] not in ("GET" , "HEAD" ):
7085 await simple_response (
7186 scope , send , status = 405 , content = "Method Not Allowed"
7287 )
7388 return
7489
75- # Serve a JS web module
90+ # Route requests to our JS web module app
7691 if scope ["type" ] == "http" and re .match (
7792 self .js_modules_path , scope ["path" ]
7893 ):
7994 await self .js_modules_app (scope , receive , send )
8095 return
8196
82- # Serve a static file
97+ # Route requests to our static file server app
8398 if scope ["type" ] == "http" and re .match (self .static_path , scope ["path" ]):
8499 await self .static_file_app (scope , receive , send )
85100 return
86101
87- # Serve index.html
88- if scope ["type" ] == "http" :
102+ # Route all other requests to serve a component (user is in standalone mode)
103+ if scope ["type" ] == "http" and self . component :
89104 await self .index_html_app (scope , receive , send )
90105 return
91106
92107 # Serve the user's application
93- else :
108+ if self . user_app :
94109 await self .user_app (scope , receive , send )
110+ return
111+
112+ _logger .error ("ReactPy appears to be misconfigured. Request not handled." )
95113
96114 async def component_dispatch_app (self , scope , receive , send ) -> None :
97115 """The ASGI application for ReactPy Python components."""
0 commit comments