-
-
Notifications
You must be signed in to change notification settings - Fork 324
Client-Side Python Components #1269
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Archmonger
merged 36 commits into
reactive-python:develop
from
Archmonger:v2-pyscript-components
Feb 10, 2025
Merged
Changes from 9 commits
Commits
Show all changes
36 commits
Select commit
Hold shift + click to select a range
58a4e37
First draft of pyscript support
Archmonger 6dea8b3
move standalone to executors module
Archmonger 9dccf73
prototype pysript executor
Archmonger e4f5901
functional rendering on standalone pyscript
Archmonger 02d114c
Fix pyscript event handling
Archmonger f30a041
Resolve type checker warnings
Archmonger e6136a0
move jinja template tag module
Archmonger 027f090
remove standard installation extra
Archmonger afbd437
automate pyscript/morphdom static building
Archmonger 6f092a8
Move optional dependencies up
Archmonger 6b1e50b
remove unused dependency
Archmonger 5afa28b
fix tests
Archmonger 2119fb1
Reduce dependencies by making ASGI optional
Archmonger 36c6642
Use local ReactPy wheel for unpublished releases.
Archmonger d944643
move asgi_component_html function
Archmonger 8f29887
remove useless async
Archmonger d5f3bec
docstring for ReactPyCSR
Archmonger 74d13ef
ReactPyCSRApp
Archmonger 208cdc6
Add JS as known third party pkg
Archmonger 4169d46
Add changelog
Archmonger 28c4374
Add JS as known third party pkg
Archmonger f0c47dc
Expose pyscript components at top level
Archmonger 9a485bd
CSR -> Pyodide
Archmonger 1e74681
Temporary fix to pyscript bug
Archmonger 459bcc5
use new pyscript syntax
Archmonger f387f6d
regex based python minification
Archmonger 16082ae
component_paths -> file_paths
Archmonger bab3d71
Add some test cases
Archmonger 9a3418f
refactor executors module
Archmonger 5f680b8
Add tests for standalone pyscript
Archmonger 5d2877b
Add configuration option to standlone reactpy to auto-load pyscript
Archmonger 1f6fb2c
Some pyscript component tests
Archmonger 23213d1
100% coverage
Archmonger 4749212
Fix type check warnings
Archmonger c9d16c3
format imports
Archmonger ea46a94
self review
Archmonger File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
from __future__ import annotations | ||
|
||
import hashlib | ||
import re | ||
from dataclasses import dataclass | ||
from datetime import datetime, timezone | ||
from email.utils import formatdate | ||
from pathlib import Path | ||
from typing import Any | ||
|
||
from asgiref.typing import WebSocketScope | ||
from typing_extensions import Unpack | ||
|
||
from reactpy import html | ||
from reactpy.asgi.executors.standalone import ReactPy, ReactPyApp | ||
from reactpy.asgi.middleware import ReactPyMiddleware | ||
from reactpy.asgi.utils import vdom_head_to_html | ||
from reactpy.pyscript.utils import pyscript_component_html, pyscript_setup_html | ||
from reactpy.types import ( | ||
ReactPyConfig, | ||
VdomDict, | ||
) | ||
|
||
|
||
class ReactPyCSR(ReactPy): | ||
def __init__( | ||
self, | ||
*component_paths: str | Path, | ||
extra_py: tuple[str, ...] = (), | ||
extra_js: dict[str, Any] | str = "", | ||
pyscript_config: dict[str, Any] | str = "", | ||
root_name: str = "root", | ||
initial: str | VdomDict = "", | ||
http_headers: dict[str, str] | None = None, | ||
html_head: VdomDict | None = None, | ||
html_lang: str = "en", | ||
**settings: Unpack[ReactPyConfig], | ||
) -> None: | ||
"""Variant of ReactPy's standalone that only performs Client-Side Rendering (CSR). | ||
|
||
Parameters: | ||
... | ||
""" | ||
ReactPyMiddleware.__init__( | ||
self, app=ReactPyAppCSR(self), root_components=[], **settings | ||
) | ||
if not component_paths: | ||
raise ValueError("At least one component file path must be provided.") | ||
self.component_paths = tuple(str(path) for path in component_paths) | ||
self.extra_py = extra_py | ||
self.extra_js = extra_js | ||
self.pyscript_config = pyscript_config | ||
self.root_name = root_name | ||
self.initial = initial | ||
self.extra_headers = http_headers or {} | ||
self.dispatcher_pattern = re.compile(f"^{self.dispatcher_path}?") | ||
self.html_head = html_head or html.head() | ||
self.html_lang = html_lang | ||
|
||
def match_dispatch_path(self, scope: WebSocketScope) -> bool: | ||
return False | ||
|
||
|
||
@dataclass | ||
class ReactPyAppCSR(ReactPyApp): | ||
"""ReactPy's standalone ASGI application for Client-Side Rendering (CSR).""" | ||
|
||
parent: ReactPyCSR | ||
_index_html = "" | ||
_etag = "" | ||
_last_modified = "" | ||
|
||
def render_index_html(self) -> None: | ||
"""Process the index.html and store the results in this class.""" | ||
head_content = vdom_head_to_html(self.parent.html_head) | ||
pyscript_setup = pyscript_setup_html( | ||
extra_py=self.parent.extra_py, | ||
extra_js=self.parent.extra_js, | ||
config=self.parent.pyscript_config, | ||
) | ||
pyscript_component = pyscript_component_html( | ||
file_paths=self.parent.component_paths, | ||
initial=self.parent.initial, | ||
root=self.parent.root_name, | ||
) | ||
head_content = head_content.replace("</head>", f"{pyscript_setup}</head>") | ||
|
||
self._index_html = ( | ||
"<!doctype html>" | ||
f'<html lang="{self.parent.html_lang}">' | ||
f"{head_content}" | ||
"<body>" | ||
f"{pyscript_component}" | ||
"</body>" | ||
"</html>" | ||
) | ||
self._etag = f'"{hashlib.md5(self._index_html.encode(), usedforsecurity=False).hexdigest()}"' | ||
self._last_modified = formatdate( | ||
datetime.now(tz=timezone.utc).timestamp(), usegmt=True | ||
) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# ruff: noqa: TC004, N802, N816, RUF006 | ||
# type: ignore | ||
from typing import TYPE_CHECKING | ||
|
||
if TYPE_CHECKING: | ||
import asyncio | ||
|
||
from reactpy.pyscript.layout_handler import ReactPyLayoutHandler | ||
|
||
|
||
# User component is inserted below by regex replacement | ||
def user_workspace_UUID(): | ||
"""Encapsulate the user's code with a completely unique function (workspace) | ||
to prevent overlapping imports and variable names between different components. | ||
|
||
This code is designed to be run directly by PyScript, and is not intended to be run | ||
in a normal Python environment. | ||
|
||
ReactPy-Django performs string substitutions to turn this file into valid PyScript. | ||
""" | ||
|
||
def root(): ... | ||
|
||
return root() | ||
|
||
|
||
# Create a task to run the user's component workspace | ||
task_UUID = asyncio.create_task(ReactPyLayoutHandler("UUID").run(user_workspace_UUID)) |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.