Skip to content

Commit 9a3418f

Browse files
committed
refactor executors module
1 parent bab3d71 commit 9a3418f

File tree

11 files changed

+81
-83
lines changed

11 files changed

+81
-83
lines changed

src/reactpy/asgi/__init__.py

Lines changed: 0 additions & 5 deletions
This file was deleted.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from reactpy.executors.asgi.middleware import ReactPyMiddleware
2+
from reactpy.executors.asgi.pyscript import ReactPyPyscript
3+
from reactpy.executors.asgi.standalone import ReactPy
4+
5+
__all__ = ["ReactPy", "ReactPyMiddleware", "ReactPyPyscript"]
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,18 @@
1818
from typing_extensions import Unpack
1919

2020
from reactpy import config
21-
from reactpy.asgi.types import (
21+
from reactpy.core.hooks import ConnectionContext
22+
from reactpy.core.layout import Layout
23+
from reactpy.core.serve import serve_layout
24+
from reactpy.executors.asgi.types import (
2225
AsgiApp,
2326
AsgiHttpApp,
2427
AsgiLifespanApp,
2528
AsgiWebsocketApp,
2629
AsgiWebsocketReceive,
2730
AsgiWebsocketSend,
2831
)
29-
from reactpy.asgi.utils import check_path, import_components, process_settings
30-
from reactpy.core.hooks import ConnectionContext
31-
from reactpy.core.layout import Layout
32-
from reactpy.core.serve import serve_layout
32+
from reactpy.executors.utils import check_path, import_components, process_settings
3333
from reactpy.types import Connection, Location, ReactPyConfig, RootComponentConstructor
3434

3535
_logger = logging.getLogger(__name__)
@@ -90,7 +90,7 @@ def __init__(
9090

9191
# Directory attributes
9292
self.web_modules_dir = config.REACTPY_WEB_MODULES_DIR.current
93-
self.static_dir = Path(__file__).parent.parent / "static"
93+
self.static_dir = Path(__file__).parent.parent.parent / "static"
9494

9595
# Initialize the sub-applications
9696
self.component_dispatch_app = ComponentDispatchApp(parent=self)
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@
1212
from typing_extensions import Unpack
1313

1414
from reactpy import html
15-
from reactpy.asgi.executors.standalone import ReactPy, ReactPyApp
16-
from reactpy.asgi.middleware import ReactPyMiddleware
17-
from reactpy.asgi.utils import vdom_head_to_html
15+
from reactpy.executors.asgi.middleware import ReactPyMiddleware
16+
from reactpy.executors.asgi.standalone import ReactPy, ReactPyApp
17+
from reactpy.executors.utils import vdom_head_to_html
1818
from reactpy.pyscript.utils import pyscript_component_html, pyscript_setup_html
1919
from reactpy.types import ReactPyConfig, VdomDict
2020

2121

22-
class ReactPyPyodide(ReactPy):
22+
class ReactPyPyscript(ReactPy):
2323
def __init__(
2424
self,
2525
*file_paths: str | Path,
@@ -34,7 +34,7 @@ def __init__(
3434
**settings: Unpack[ReactPyConfig],
3535
) -> None:
3636
"""Variant of ReactPy's standalone that only performs Client-Side Rendering (CSR) via
37-
Pyodide (using the PyScript API).
37+
PyScript (using the Pyodide interpreter).
3838
3939
This ASGI webserver is only used to serve the initial HTML document and static files.
4040
@@ -87,7 +87,7 @@ def match_dispatch_path(self, scope: WebSocketScope) -> bool:
8787
class ReactPyPyodideApp(ReactPyApp):
8888
"""ReactPy's standalone ASGI application for Client-Side Rendering (CSR)."""
8989

90-
parent: ReactPyPyodide
90+
parent: ReactPyPyscript
9191
_index_html = ""
9292
_etag = ""
9393
_last_modified = ""
Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,16 @@
1313
from typing_extensions import Unpack
1414

1515
from reactpy import html
16-
from reactpy.asgi.middleware import ReactPyMiddleware
17-
from reactpy.asgi.types import AsgiApp, AsgiHttpApp, AsgiLifespanApp, AsgiWebsocketApp
18-
from reactpy.asgi.utils import (
19-
asgi_component_html,
20-
import_dotted_path,
21-
vdom_head_to_html,
16+
from reactpy.executors.asgi.middleware import ReactPyMiddleware
17+
from reactpy.executors.asgi.types import (
18+
AsgiApp,
19+
AsgiHttpApp,
20+
AsgiLifespanApp,
21+
AsgiWebsocketApp,
2222
)
23+
from reactpy.executors.utils import server_side_component_html, vdom_head_to_html
2324
from reactpy.types import ReactPyConfig, RootComponentConstructor, VdomDict
25+
from reactpy.utils import import_dotted_path
2426

2527
_logger = getLogger(__name__)
2628

@@ -209,7 +211,7 @@ def render_index_html(self) -> None:
209211
f'<html lang="{self.parent.html_lang}">'
210212
f"{vdom_head_to_html(self.parent.html_head)}"
211213
"<body>"
212-
f"{asgi_component_html(element_id='app', class_='', component_path='')}"
214+
f"{server_side_component_html(element_id='app', class_='', component_path='')}"
213215
"</body>"
214216
"</html>"
215217
)
Lines changed: 4 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import logging
44
from collections.abc import Iterable
5-
from importlib import import_module
65
from typing import Any
76

87
from reactpy._option import Option
@@ -14,31 +13,11 @@
1413
REACTPY_RECONNECT_MAX_RETRIES,
1514
)
1615
from reactpy.types import ReactPyConfig, VdomDict
17-
from reactpy.utils import vdom_to_html
16+
from reactpy.utils import import_dotted_path, vdom_to_html
1817

1918
logger = logging.getLogger(__name__)
2019

2120

22-
def import_dotted_path(dotted_path: str) -> Any:
23-
"""Imports a dotted path and returns the callable."""
24-
if "." not in dotted_path:
25-
raise ValueError(f'"{dotted_path}" is not a valid dotted path.')
26-
27-
module_name, component_name = dotted_path.rsplit(".", 1)
28-
29-
try:
30-
module = import_module(module_name)
31-
except ImportError as error:
32-
msg = f'ReactPy failed to import "{module_name}"'
33-
raise ImportError(msg) from error
34-
35-
try:
36-
return getattr(module, component_name)
37-
except AttributeError as error:
38-
msg = f'ReactPy failed to import "{component_name}" from "{module_name}"'
39-
raise AttributeError(msg) from error
40-
41-
4221
def import_components(dotted_paths: Iterable[str]) -> dict[str, Any]:
4322
"""Imports a list of dotted paths and returns the callables."""
4423
return {
@@ -82,7 +61,9 @@ def process_settings(settings: ReactPyConfig) -> None:
8261
raise ValueError(f'Unknown ReactPy setting "{setting}".')
8362

8463

85-
def asgi_component_html(element_id: str, class_: str, component_path: str) -> str:
64+
def server_side_component_html(
65+
element_id: str, class_: str, component_path: str
66+
) -> str:
8667
return (
8768
f'<div id="{element_id}" class="{class_}"></div>'
8869
'<script type="module" crossorigin="anonymous">'

src/reactpy/utils.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import re
44
from collections.abc import Iterable
5+
from importlib import import_module
56
from itertools import chain
67
from typing import Any, Callable, Generic, TypeVar, Union, cast
78

@@ -313,3 +314,23 @@ def _vdom_attr_to_html_str(key: str, value: Any) -> tuple[str, str]:
313314

314315
# Pattern for delimitting camelCase names (e.g. camelCase to camel-case)
315316
_CAMEL_CASE_SUB_PATTERN = re.compile(r"(?<!^)(?=[A-Z])")
317+
318+
319+
def import_dotted_path(dotted_path: str) -> Any:
320+
"""Imports a dotted path and returns the callable."""
321+
if "." not in dotted_path:
322+
raise ValueError(f'"{dotted_path}" is not a valid dotted path.')
323+
324+
module_name, component_name = dotted_path.rsplit(".", 1)
325+
326+
try:
327+
module = import_module(module_name)
328+
except ImportError as error:
329+
msg = f'ReactPy failed to import "{module_name}"'
330+
raise ImportError(msg) from error
331+
332+
try:
333+
return getattr(module, component_name)
334+
except AttributeError as error:
335+
msg = f'ReactPy failed to import "{component_name}" from "{module_name}"'
336+
raise AttributeError(msg) from error

tests/test_asgi/test_utils.py

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,7 @@
11
import pytest
22

33
from reactpy import config
4-
from reactpy.asgi import utils
5-
6-
7-
def test_invalid_dotted_path():
8-
with pytest.raises(ValueError, match='"abc" is not a valid dotted path.'):
9-
utils.import_dotted_path("abc")
10-
11-
12-
def test_invalid_component():
13-
with pytest.raises(
14-
AttributeError, match='ReactPy failed to import "foobar" from "reactpy"'
15-
):
16-
utils.import_dotted_path("reactpy.foobar")
17-
18-
19-
def test_invalid_module():
20-
with pytest.raises(ImportError, match='ReactPy failed to import "foo"'):
21-
utils.import_dotted_path("foo.bar")
4+
from reactpy.executors import utils
225

236

247
def test_invalid_vdom_head():

0 commit comments

Comments
 (0)