Skip to content

Commit bde42aa

Browse files
committed
More robust route handling
1 parent 16ada33 commit bde42aa

File tree

1 file changed

+28
-10
lines changed
  • src/py/reactpy/reactpy/backend

1 file changed

+28
-10
lines changed

src/py/reactpy/reactpy/backend/asgi.py

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import urllib.parse
77
from collections.abc import Sequence
88
from pathlib import Path
9+
from typing import Coroutine
910

1011
import aiofiles
1112
import orjson
@@ -17,12 +18,12 @@
1718
vdom_head_elements_to_html,
1819
)
1920
from reactpy.backend.hooks import ConnectionContext
20-
from reactpy.backend.mimetypes import DEFAULT_MIME_TYPES
21+
from reactpy.backend.mimetypes import MIME_TYPES
2122
from reactpy.backend.types import Connection, Location
2223
from reactpy.config import REACTPY_WEB_MODULES_DIR
2324
from reactpy.core.layout import Layout
2425
from reactpy.core.serve import serve_layout
25-
from reactpy.core.types import VdomDict
26+
from reactpy.core.types import ComponentConstructor, VdomDict
2627

2728
DEFAULT_STATIC_PATH = f"{os.getcwd()}/static"
2829
DEFAULT_BLOCK_SIZE = 8192
@@ -32,14 +33,28 @@
3233
class 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

Comments
 (0)