Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,8 @@ omit = [
"src/reactpy/__init__.py",
"src/reactpy/_console/*",
"src/reactpy/__main__.py",
"src/reactpy/pyscript/layout_handler.py",
"src/reactpy/pyscript/component_template.py",
"src/reactpy/executors/pyscript/layout_handler.py",
"src/reactpy/executors/pyscript/component_template.py",
]

[tool.coverage.report]
Expand Down
2 changes: 1 addition & 1 deletion src/build_scripts/install_playwright.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@
# as *nix systems (such as WSL) return a failure code if there are *any* dependencies
# that could be cleaned up via `sudo apt autoremove`. This occurs even if we weren't
# the ones to install those dependencies in the first place.
subprocess.run(["playwright", "install-deps"], check=False) # noqa: S607
subprocess.run(["playwright", "install-deps", "chromium"], check=False) # noqa: S607
9 changes: 3 additions & 6 deletions src/js/packages/@reactpy/client/src/bind.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,11 @@ export async function infer_bind_from_environment() {
const React = await import("react");
// @ts-ignore
const ReactDOM = await import("react-dom/client");

console.log(
"ReactPy detected 'ReactJS' to bind your JavaScript components.",
);
return (node: HTMLElement) => reactjs_bind(node, React, ReactDOM);
} catch {
console.debug(
"ReactPy did not detect a component binding function or a suitable 'importmap'. Using ReactPy's internal framework (Preact) to bind your JavaScript components.",
console.error(
"Unknown error occurred: 'react' is missing within this ReactPy environment! \
Your JavaScript components may not work as expected!",
);
return (node: HTMLElement) => local_preact_bind(node);
}
Expand Down
6 changes: 3 additions & 3 deletions src/js/packages/@reactpy/client/src/components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import type {
ImportSourceBinding,
ReactPyComponent,
ReactPyVdom,
ReactPyClientInterface,
} from "./types";
import { createAttributes, createChildren, loadImportSource } from "./vdom";
import type { ReactPyClient } from "./client";

const ClientContext = createContext<ReactPyClientInterface>(null as any);
const ClientContext = createContext<ReactPyClient>(null as any);

export function Layout(props: { client: ReactPyClientInterface }): JSX.Element {
export function Layout(props: { client: ReactPyClient }): JSX.Element {
const currentModel: ReactPyVdom = useState({ tagName: "" })[0];
const forceUpdate = useForceUpdate();

Expand Down
14 changes: 7 additions & 7 deletions src/js/packages/@reactpy/client/src/vdom.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type { ReactPyClientInterface } from "./types";
import eventToObject from "event-to-object";
import type {
ReactPyVdom,
Expand All @@ -11,10 +10,11 @@ import type {
} from "./types";
import { infer_bind_from_environment } from "./bind";
import log from "./logger";
import type { ReactPyClient } from "./client";

export async function loadImportSource(
vdomImportSource: ReactPyVdomImportSource,
client: ReactPyClientInterface,
client: ReactPyClient,
): Promise<BindImportSource> {
let module: ReactPyModule;
if (vdomImportSource.sourceType === "URL") {
Expand Down Expand Up @@ -61,7 +61,7 @@ export async function loadImportSource(
}

function createImportSourceElement(props: {
client: ReactPyClientInterface;
client: ReactPyClient;
module: ReactPyModule;
binding: ReactPyModuleBinding;
model: ReactPyVdom;
Expand Down Expand Up @@ -180,7 +180,7 @@ export function createChildren<Child>(

export function createAttributes(
model: ReactPyVdom,
client: ReactPyClientInterface,
client: ReactPyClient,
): { [key: string]: any } {
return Object.fromEntries(
Object.entries({
Expand All @@ -203,7 +203,7 @@ export function createAttributes(
}

function createEventHandler(
client: ReactPyClientInterface,
client: ReactPyClient,
name: string,
{ target, preventDefault, stopPropagation }: ReactPyVdomEventHandler,
): [string, () => void] {
Expand Down Expand Up @@ -262,7 +262,7 @@ function createInlineJavaScript(
class ReactPyChild extends HTMLElement {
mountPoint: HTMLDivElement;
binding: ImportSourceBinding | null = null;
_client: ReactPyClientInterface | null = null;
_client: ReactPyClient | null = null;
_model: ReactPyVdom | null = null;
currentImportSource: ReactPyVdomImportSource | null = null;

Expand All @@ -276,7 +276,7 @@ class ReactPyChild extends HTMLElement {
this.appendChild(this.mountPoint);
}

set client(value: ReactPyClientInterface) {
set client(value: ReactPyClient) {
this._client = value;
}

Expand Down
4 changes: 2 additions & 2 deletions src/reactpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@
use_state,
)
from reactpy.core.vdom import Vdom
from reactpy.pyscript.components import pyscript_component
from reactpy.executors.pyscript.components import pyscript_component
from reactpy.utils import Ref, reactpy_to_string, string_to_reactpy

__author__ = "The Reactive Python Team"
__version__ = "2.0.0b6"
__version__ = "2.0.0b7"

__all__ = [
"Ref",
Expand Down
2 changes: 1 addition & 1 deletion src/reactpy/core/_thread_local.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

class ThreadLocal(Generic[_StateType]): # nocov
"""Utility for managing per-thread state information. This is only used in
environments where ContextVars are not available, such as the `pyodide`
environments where ContextVars are not available, such as the `pyscript`
executor."""

def __init__(self, default: Callable[[], _StateType]):
Expand Down
30 changes: 11 additions & 19 deletions src/reactpy/core/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,7 @@ def __init__(
self,
initial_value: _Type | Callable[[], _Type],
) -> None:
if callable(initial_value):
self.value = initial_value()
else:
self.value = initial_value

self.value = initial_value() if callable(initial_value) else initial_value
hook = HOOK_STACK.current_hook()

def dispatch(new: _Type | Callable[[_Type], _Type]) -> None:
Expand Down Expand Up @@ -434,10 +430,7 @@ def use_callback(
def setup(function: _CallbackFunc) -> _CallbackFunc:
return memoize(lambda: function)

if function is not None:
return setup(function)
else:
return setup
return setup(function) if function is not None else setup


class _LambdaCaller(Protocol):
Expand Down Expand Up @@ -553,17 +546,16 @@ def _try_to_infer_closure_values(
func: Callable[..., Any] | None,
values: Sequence[Any] | ellipsis | None,
) -> Sequence[Any] | None:
if values is ...:
if isinstance(func, FunctionType):
return (
[cell.cell_contents for cell in func.__closure__]
if func.__closure__
else []
)
else:
return None
else:
if values is not ...:
return values
if isinstance(func, FunctionType):
return (
[cell.cell_contents for cell in func.__closure__]
if func.__closure__
else []
)
else:
return None


def strictly_equal(x: Any, y: Any) -> bool:
Expand Down
5 changes: 4 additions & 1 deletion src/reactpy/executors/asgi/pyscript.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@
from reactpy.executors.asgi.middleware import ReactPyMiddleware
from reactpy.executors.asgi.standalone import ReactPy, ReactPyApp
from reactpy.executors.asgi.types import AsgiWebsocketScope
from reactpy.executors.pyscript.utils import (
pyscript_component_html,
pyscript_setup_html,
)
from reactpy.executors.utils import vdom_head_to_html
from reactpy.pyscript.utils import pyscript_component_html, pyscript_setup_html
from reactpy.types import ReactPyConfig, VdomDict


Expand Down
2 changes: 1 addition & 1 deletion src/reactpy/executors/asgi/standalone.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
AsgiV3WebsocketApp,
AsgiWebsocketScope,
)
from reactpy.executors.pyscript.utils import pyscript_setup_html
from reactpy.executors.utils import server_side_component_html, vdom_head_to_html
from reactpy.pyscript.utils import pyscript_setup_html
from reactpy.types import (
PyScriptOptions,
ReactPyConfig,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# type: ignore
import asyncio

from reactpy.pyscript.layout_handler import ReactPyLayoutHandler
from reactpy.executors.pyscript.layout_handler import ReactPyLayoutHandler


# User component is inserted below by regex replacement
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from typing import TYPE_CHECKING

from reactpy import component, hooks
from reactpy.pyscript.utils import pyscript_component_html
from reactpy.executors.pyscript.utils import pyscript_component_html
from reactpy.types import Component, Key
from reactpy.utils import string_to_reactpy

Expand Down
File renamed without changes.
39 changes: 32 additions & 7 deletions src/reactpy/executors/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,16 +64,41 @@ def server_side_component_html(
) -> str:
return (
f'<div id="{element_id}" class="{class_}"></div>'
"<script>"
'if (!document.querySelector("#reactpy-importmap")) {'
" console.debug("
' "ReactPy did not detect a suitable JavaScript import map. Falling back to ReactPy\'s internal framework (Preact)."'
" );"
' const im = document.createElement("script");'
' im.type = "importmap";'
f" im.textContent = '{default_import_map()}';"
' im.id = "reactpy-importmap";'
" document.head.appendChild(im);"
" delete im;"
"}"
"</script>"
'<script type="module" crossorigin="anonymous">'
f'import {{ mountReactPy }} from "{REACTPY_PATH_PREFIX.current}static/index.js";'
"mountReactPy({"
f' mountElement: document.getElementById("{element_id}"),'
f' pathPrefix: "{REACTPY_PATH_PREFIX.current}",'
f' componentPath: "{component_path}",'
f" reconnectInterval: {REACTPY_RECONNECT_INTERVAL.current},"
f" reconnectMaxInterval: {REACTPY_RECONNECT_MAX_INTERVAL.current},"
f" reconnectMaxRetries: {REACTPY_RECONNECT_MAX_RETRIES.current},"
f" reconnectBackoffMultiplier: {REACTPY_RECONNECT_BACKOFF_MULTIPLIER.current},"
f' mountElement: document.getElementById("{element_id}"),'
f' pathPrefix: "{REACTPY_PATH_PREFIX.current}",'
f' componentPath: "{component_path}",'
f" reconnectInterval: {REACTPY_RECONNECT_INTERVAL.current},"
f" reconnectMaxInterval: {REACTPY_RECONNECT_MAX_INTERVAL.current},"
f" reconnectMaxRetries: {REACTPY_RECONNECT_MAX_RETRIES.current},"
f" reconnectBackoffMultiplier: {REACTPY_RECONNECT_BACKOFF_MULTIPLIER.current},"
"});"
"</script>"
)


def default_import_map() -> str:
path_prefix = REACTPY_PATH_PREFIX.current.strip("/")
return f"""{{
"imports": {{
"react": "/{path_prefix}/static/preact.js",
"react-dom": "/{path_prefix}/static/preact-dom.js",
"react-dom/client": "/{path_prefix}/static/preact-dom.js",
"react/jsx-runtime": "/{path_prefix}/static/preact-jsx-runtime.js"
}}
}}""".replace("\n", "").replace(" ", "")
36 changes: 11 additions & 25 deletions src/reactpy/reactjs/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from pathlib import Path, PurePosixPath
from typing import Any, Literal

from reactpy import config
from reactpy.config import REACTPY_DEBUG, REACTPY_WEB_MODULES_DIR
from reactpy.core.vdom import Vdom
from reactpy.reactjs.types import NAME_SOURCE, URL_SOURCE
Expand All @@ -15,7 +14,7 @@
resolve_names_from_file,
resolve_names_from_url,
)
from reactpy.types import ImportSourceDict, JavaScriptModule, VdomConstructor
from reactpy.types import ImportSourceDict, JavaScriptModule, VdomConstructor, VdomDict

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -183,7 +182,7 @@ def import_reactjs(
framework: Literal["preact", "react"] | None = None,
version: str | None = None,
use_local: bool = False,
):
) -> VdomDict:
"""
Return an import map script tag for ReactJS or Preact.
Parameters:
Expand All @@ -205,6 +204,7 @@ def import_reactjs(
A VDOM script tag containing the import map.
"""
from reactpy import html
from reactpy.executors.utils import default_import_map

if use_local and (framework or version): # nocov
raise ValueError("use_local cannot be used with framework or version")
Expand All @@ -215,55 +215,41 @@ def import_reactjs(

# Import map for ReactPy's local framework (re-exported/bundled/minified version of Preact)
if use_local:
prefix = f"/{config.REACTPY_PATH_PREFIX.current.strip('/')}/static/{framework}"
return html.script(
{"type": "importmap"},
f"""
{{
"imports": {{
"react": "{prefix}.js",
"react-dom": "{prefix}-dom.js",
"react-dom/client": "{prefix}-dom.js",
"react/jsx-runtime": "{prefix}-jsx-runtime.js"
}}
}}
""",
{"type": "importmap", "id": "reactpy-importmap"},
default_import_map(),
)

# Import map for ReactJS from esm.sh
if framework == "react":
version = version or "19"
postfix = "?dev" if REACTPY_DEBUG.current else ""
return html.script(
{"type": "importmap"},
f"""
{{
{"type": "importmap", "id": "reactpy-importmap"},
f"""{{
"imports": {{
"react": "https://esm.sh/react@{version}{postfix}",
"react-dom": "https://esm.sh/react-dom@{version}{postfix}",
"react-dom/client": "https://esm.sh/react-dom@{version}/client{postfix}",
"react/jsx-runtime": "https://esm.sh/react@{version}/jsx-runtime{postfix}"
}}
}}
""",
}}""".replace("\n", "").replace(" ", ""),
)

# Import map for Preact from esm.sh
if framework == "preact":
version = version or "10"
postfix = "?dev" if REACTPY_DEBUG.current else ""
return html.script(
{"type": "importmap"},
f"""
{{
{"type": "importmap", "id": "reactpy-importmap"},
f"""{{
"imports": {{
"react": "https://esm.sh/preact@{version}/compat{postfix}",
"react-dom": "https://esm.sh/preact@{version}/compat{postfix}",
"react-dom/client": "https://esm.sh/preact@{version}/compat/client{postfix}",
"react/jsx-runtime": "https://esm.sh/preact@{version}/compat/jsx-runtime{postfix}"
}}
}}
""",
}}""".replace("\n", "").replace(" ", ""),
)


Expand Down
5 changes: 4 additions & 1 deletion src/reactpy/templatetags/jinja.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@

from jinja2_simple_tags import StandaloneTag

from reactpy.executors.pyscript.utils import (
pyscript_component_html,
pyscript_setup_html,
)
from reactpy.executors.utils import server_side_component_html
from reactpy.pyscript.utils import pyscript_component_html, pyscript_setup_html


class ReactPyJinja(StandaloneTag): # type: ignore
Expand Down
Loading
Loading