From 8dba3d3a0d7d4e98f61a144a69bce3ca2b3c995b Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Sun, 15 Jun 2025 02:45:40 -0700 Subject: [PATCH 1/4] Changes to v2 to encourage docs simplicity --- docs/source/about/changelog.rst | 10 +++++----- pyproject.toml | 3 +-- src/reactpy/executors/asgi/__init__.py | 4 ++-- src/reactpy/executors/asgi/pyscript.py | 8 ++++---- src/reactpy/pyscript/component_template.py | 9 +++------ src/reactpy/pyscript/layout_handler.py | 2 +- src/reactpy/templatetags/__init__.py | 4 ++-- src/reactpy/templatetags/jinja.py | 2 +- tests/test_asgi/test_middleware.py | 6 +++--- tests/test_asgi/test_pyscript.py | 10 +++++----- 10 files changed, 27 insertions(+), 31 deletions(-) diff --git a/docs/source/about/changelog.rst b/docs/source/about/changelog.rst index bb4ea5e7f..ad36a447a 100644 --- a/docs/source/about/changelog.rst +++ b/docs/source/about/changelog.rst @@ -18,11 +18,11 @@ Unreleased **Added** - :pull:`1113` - Added ``reactpy.executors.asgi.ReactPy`` that can be used to run ReactPy in standalone mode via ASGI. -- :pull:`1269` - Added ``reactpy.executors.asgi.ReactPyPyodide`` that can be used to run ReactPy in standalone mode via ASGI, but rendered entirely client-sided. +- :pull:`1269` - Added ``reactpy.executors.asgi.ReactPyCsr`` that can be used to run ReactPy in standalone mode via ASGI, but rendered entirely client-sided. - :pull:`1113` - Added ``reactpy.executors.asgi.ReactPyMiddleware`` that can be used to utilize ReactPy within any ASGI compatible framework. -- :pull:`1269` - Added ``reactpy.templatetags.Jinja`` that can be used alongside ``ReactPyMiddleware`` to embed several ReactPy components into your existing application. This includes the following template tags: ``{% component %}``, ``{% pyscript_component %}``, and ``{% pyscript_setup %}``. +- :pull:`1269` - Added ``reactpy.templatetags.ReactPyJinja`` that can be used alongside ``ReactPyMiddleware`` to embed several ReactPy components into your existing application. This includes the following template tags: ``{% component %}``, ``{% pyscript_component %}``, and ``{% pyscript_setup %}``. - :pull:`1269` - Added ``reactpy.pyscript_component`` that can be used to embed ReactPy components into your existing application. -- :pull:`1113` - Added ``uvicorn`` and ``jinja`` installation extras (for example ``pip install reactpy[jinja]``). +- :pull:`1113` - Added ``asgi`` and ``jinja`` installation extras (for example ``pip install reactpy[asgi, jinja]``). - :pull:`1113` - Added support for Python 3.12 and 3.13. - :pull:`1264` - Added ``reactpy.use_async_effect`` hook. - :pull:`1267` - Added ``shutdown_timeout`` parameter to the ``reactpy.use_async_effect`` hook. @@ -72,8 +72,8 @@ Unreleased **Fixed** - :pull:`1239` - Fixed a bug where script elements would not render to the DOM as plain text. -- :pull:`1271` - Fixed a bug where the ``key`` property provided via server-side ReactPy code was failing to propagate to the front-end JavaScript component. -- :pull:`1254` - Fixed a bug where ``RuntimeError("Hook stack is in an invalid state")`` errors would be provided when using a webserver that reuses threads. +- :pull:`1271` - Fixed a bug where the ``key`` property provided within server-side ReactPy code was failing to propagate to the front-end JavaScript components. +- :pull:`1254` - Fixed a bug where ``RuntimeError("Hook stack is in an invalid state")`` errors could be generated when using a webserver that reuses threads. v1.1.0 ------ diff --git a/pyproject.toml b/pyproject.toml index 173fdb173..d6d33bbc0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,10 +40,9 @@ urls.Documentation = "https://reactpy.dev/" urls.Source = "https://github.com/reactive-python/reactpy" [project.optional-dependencies] -all = ["reactpy[asgi,jinja,uvicorn,testing]"] +all = ["reactpy[asgi,jinja,testing]"] asgi = ["asgiref", "asgi-tools", "servestatic", "orjson", "pip"] jinja = ["jinja2-simple-tags", "jinja2 >=3"] -uvicorn = ["uvicorn[standard]"] testing = ["playwright"] [tool.hatch.version] diff --git a/src/reactpy/executors/asgi/__init__.py b/src/reactpy/executors/asgi/__init__.py index e7c9716af..e43d00cb8 100644 --- a/src/reactpy/executors/asgi/__init__.py +++ b/src/reactpy/executors/asgi/__init__.py @@ -1,5 +1,5 @@ from reactpy.executors.asgi.middleware import ReactPyMiddleware -from reactpy.executors.asgi.pyscript import ReactPyPyscript +from reactpy.executors.asgi.pyscript import ReactPyCsr from reactpy.executors.asgi.standalone import ReactPy -__all__ = ["ReactPy", "ReactPyMiddleware", "ReactPyPyscript"] +__all__ = ["ReactPy", "ReactPyCsr", "ReactPyMiddleware"] diff --git a/src/reactpy/executors/asgi/pyscript.py b/src/reactpy/executors/asgi/pyscript.py index b3f2cd38f..69973517c 100644 --- a/src/reactpy/executors/asgi/pyscript.py +++ b/src/reactpy/executors/asgi/pyscript.py @@ -20,7 +20,7 @@ from reactpy.types import ReactPyConfig, VdomDict -class ReactPyPyscript(ReactPy): +class ReactPyCsr(ReactPy): def __init__( self, *file_paths: str | Path, @@ -64,7 +64,7 @@ def __init__( are not applicable to CSR and will have no effect. """ ReactPyMiddleware.__init__( - self, app=ReactPyPyscriptApp(self), root_components=[], **settings + self, app=ReactpyPyscriptApp(self), root_components=[], **settings ) if not file_paths: raise ValueError("At least one component file path must be provided.") @@ -85,10 +85,10 @@ def match_dispatch_path(self, scope: AsgiWebsocketScope) -> bool: # nocov @dataclass -class ReactPyPyscriptApp(ReactPyApp): +class ReactpyPyscriptApp(ReactPyApp): """ReactPy's standalone ASGI application for Client-Side Rendering (CSR) via PyScript.""" - parent: ReactPyPyscript + parent: ReactPyCsr _index_html = "" _etag = "" _last_modified = "" diff --git a/src/reactpy/pyscript/component_template.py b/src/reactpy/pyscript/component_template.py index 47bf4d6a3..6855858fa 100644 --- a/src/reactpy/pyscript/component_template.py +++ b/src/reactpy/pyscript/component_template.py @@ -1,11 +1,8 @@ -# ruff: noqa: TC004, N802, N816, RUF006 +# ruff: noqa: N802, N816, RUF006 # type: ignore -from typing import TYPE_CHECKING +import asyncio -if TYPE_CHECKING: - import asyncio - - from reactpy.pyscript.layout_handler import ReactPyLayoutHandler +from reactpy.pyscript.layout_handler import ReactPyLayoutHandler # User component is inserted below by regex replacement diff --git a/src/reactpy/pyscript/layout_handler.py b/src/reactpy/pyscript/layout_handler.py index 733ab064f..1c2e6367a 100644 --- a/src/reactpy/pyscript/layout_handler.py +++ b/src/reactpy/pyscript/layout_handler.py @@ -11,7 +11,7 @@ class ReactPyLayoutHandler: - """Encapsulate the entire layout handler with a class to prevent overlapping + """Encapsulate the entire PyScript layout handler with a class to prevent overlapping variable names between user code. This code is designed to be run directly by PyScript, and is not intended to be run diff --git a/src/reactpy/templatetags/__init__.py b/src/reactpy/templatetags/__init__.py index c6f792d27..c9e5f28bc 100644 --- a/src/reactpy/templatetags/__init__.py +++ b/src/reactpy/templatetags/__init__.py @@ -1,3 +1,3 @@ -from reactpy.templatetags.jinja import Jinja +from reactpy.templatetags.jinja import ReactPyJinja -__all__ = ["Jinja"] +__all__ = ["ReactPyJinja"] diff --git a/src/reactpy/templatetags/jinja.py b/src/reactpy/templatetags/jinja.py index c4256b525..e2cf83489 100644 --- a/src/reactpy/templatetags/jinja.py +++ b/src/reactpy/templatetags/jinja.py @@ -7,7 +7,7 @@ from reactpy.pyscript.utils import pyscript_component_html, pyscript_setup_html -class Jinja(StandaloneTag): # type: ignore +class ReactPyJinja(StandaloneTag): # type: ignore safe_output = True tags: ClassVar[set[str]] = {"component", "pyscript_component", "pyscript_setup"} diff --git a/tests/test_asgi/test_middleware.py b/tests/test_asgi/test_middleware.py index 2ed2a3878..fc76571eb 100644 --- a/tests/test_asgi/test_middleware.py +++ b/tests/test_asgi/test_middleware.py @@ -22,7 +22,7 @@ async def display(page): templates = Jinja2Templates( env=JinjaEnvironment( loader=JinjaFileSystemLoader("tests/templates"), - extensions=["reactpy.templatetags.Jinja"], + extensions=["reactpy.templatetags.ReactPyJinja"], ) ) @@ -60,7 +60,7 @@ async def test_unregistered_root_component(): templates = Jinja2Templates( env=JinjaEnvironment( loader=JinjaFileSystemLoader("tests/templates"), - extensions=["reactpy.templatetags.Jinja"], + extensions=["reactpy.templatetags.ReactPyJinja"], ) ) @@ -124,7 +124,7 @@ async def test_templatetag_bad_kwargs(page, caplog): templates = Jinja2Templates( env=JinjaEnvironment( loader=JinjaFileSystemLoader("tests/templates"), - extensions=["reactpy.templatetags.Jinja"], + extensions=["reactpy.templatetags.ReactPyJinja"], ) ) diff --git a/tests/test_asgi/test_pyscript.py b/tests/test_asgi/test_pyscript.py index c9315e4fe..0ddd05485 100644 --- a/tests/test_asgi/test_pyscript.py +++ b/tests/test_asgi/test_pyscript.py @@ -9,14 +9,14 @@ from starlette.templating import Jinja2Templates from reactpy import html -from reactpy.executors.asgi.pyscript import ReactPyPyscript +from reactpy.executors.asgi.pyscript import ReactPyCsr from reactpy.testing import BackendFixture, DisplayFixture @pytest.fixture() async def display(page): """Override for the display fixture that uses ReactPyMiddleware.""" - app = ReactPyPyscript( + app = ReactPyCsr( Path(__file__).parent / "pyscript_components" / "root.py", initial=html.div({"id": "loading"}, "Loading..."), ) @@ -29,7 +29,7 @@ async def display(page): @pytest.fixture() async def multi_file_display(page): """Override for the display fixture that uses ReactPyMiddleware.""" - app = ReactPyPyscript( + app = ReactPyCsr( Path(__file__).parent / "pyscript_components" / "load_first.py", Path(__file__).parent / "pyscript_components" / "load_second.py", initial=html.div({"id": "loading"}, "Loading..."), @@ -46,7 +46,7 @@ async def jinja_display(page): templates = Jinja2Templates( env=JinjaEnvironment( loader=JinjaFileSystemLoader("tests/templates"), - extensions=["reactpy.templatetags.Jinja"], + extensions=["reactpy.templatetags.ReactPyJinja"], ) ) @@ -93,7 +93,7 @@ async def test_multi_file_components(multi_file_display: DisplayFixture): def test_bad_file_path(): with pytest.raises(ValueError): - ReactPyPyscript() + ReactPyCsr() async def test_jinja_template_tag(jinja_display: DisplayFixture): From cc3dfbdda6f694bffee43e717fd8e3cdb1802e29 Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Sun, 15 Jun 2025 02:53:17 -0700 Subject: [PATCH 2/4] fix missing test dependency --- docs/source/about/changelog.rst | 2 +- pyproject.toml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/source/about/changelog.rst b/docs/source/about/changelog.rst index ad36a447a..d5215177c 100644 --- a/docs/source/about/changelog.rst +++ b/docs/source/about/changelog.rst @@ -61,7 +61,7 @@ Unreleased - :pull:`1113` - Removed ``reactpy.core.types`` module. Use ``reactpy.types`` instead. - :pull:`1278` - Removed ``reactpy.utils.html_to_vdom``. Use ``reactpy.utils.string_to_reactpy`` instead. - :pull:`1278` - Removed ``reactpy.utils.vdom_to_html``. Use ``reactpy.utils.reactpy_to_string`` instead. -- :pull:`1113` - All backend related installation extras (such as ``pip install reactpy[starlette]``) have been removed. +- :pull:`1113` - Removed all backend related installation extras (such as ``pip install reactpy[starlette]``). - :pull:`1113` - Removed deprecated function ``module_from_template``. - :pull:`1113` - Removed support for Python 3.9. - :pull:`1264` - Removed support for async functions within ``reactpy.use_effect`` hook. Use ``reactpy.use_async_effect`` instead. diff --git a/pyproject.toml b/pyproject.toml index d6d33bbc0..14d9db381 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -86,6 +86,7 @@ artifacts = [] [tool.hatch.envs.hatch-test] extra-dependencies = [ "reactpy[all]", + "uvicorn", "pytest-sugar", "pytest-asyncio", "responses", From f56cc67730551dd134c26764fe62f2d48d94c52f Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Sun, 15 Jun 2025 02:56:55 -0700 Subject: [PATCH 3/4] Move uvicorn to testing deps --- pyproject.toml | 3 +-- src/reactpy/executors/asgi/pyscript.py | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 14d9db381..363f9c784 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,7 +43,7 @@ urls.Source = "https://github.com/reactive-python/reactpy" all = ["reactpy[asgi,jinja,testing]"] asgi = ["asgiref", "asgi-tools", "servestatic", "orjson", "pip"] jinja = ["jinja2-simple-tags", "jinja2 >=3"] -testing = ["playwright"] +testing = ["playwright, uvicorn[standard]"] [tool.hatch.version] path = "src/reactpy/__init__.py" @@ -86,7 +86,6 @@ artifacts = [] [tool.hatch.envs.hatch-test] extra-dependencies = [ "reactpy[all]", - "uvicorn", "pytest-sugar", "pytest-asyncio", "responses", diff --git a/src/reactpy/executors/asgi/pyscript.py b/src/reactpy/executors/asgi/pyscript.py index 69973517c..80e8b7866 100644 --- a/src/reactpy/executors/asgi/pyscript.py +++ b/src/reactpy/executors/asgi/pyscript.py @@ -64,7 +64,7 @@ def __init__( are not applicable to CSR and will have no effect. """ ReactPyMiddleware.__init__( - self, app=ReactpyPyscriptApp(self), root_components=[], **settings + self, app=ReactPyPyscriptApp(self), root_components=[], **settings ) if not file_paths: raise ValueError("At least one component file path must be provided.") @@ -85,7 +85,7 @@ def match_dispatch_path(self, scope: AsgiWebsocketScope) -> bool: # nocov @dataclass -class ReactpyPyscriptApp(ReactPyApp): +class ReactPyPyscriptApp(ReactPyApp): """ReactPy's standalone ASGI application for Client-Side Rendering (CSR) via PyScript.""" parent: ReactPyCsr From f4ec27d96857d9fa16e075df919ce26da5fe6982 Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Sun, 15 Jun 2025 03:00:02 -0700 Subject: [PATCH 4/4] fix typo --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 363f9c784..26312f8da 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,7 +43,7 @@ urls.Source = "https://github.com/reactive-python/reactpy" all = ["reactpy[asgi,jinja,testing]"] asgi = ["asgiref", "asgi-tools", "servestatic", "orjson", "pip"] jinja = ["jinja2-simple-tags", "jinja2 >=3"] -testing = ["playwright, uvicorn[standard]"] +testing = ["playwright", "uvicorn[standard]"] [tool.hatch.version] path = "src/reactpy/__init__.py"