diff --git a/.github/workflows/.hatch-run.yml b/.github/workflows/.hatch-run.yml index 027ed6b1b..ec72b1860 100644 --- a/.github/workflows/.hatch-run.yml +++ b/.github/workflows/.hatch-run.yml @@ -41,6 +41,12 @@ jobs: with: path: C:\Users\runneradmin\AppData\Local\ms-playwright\ key: ${{ runner.os }}-playwright + # FIXME: Temporarily added setup-node to fix lack of "Trusted Publishing" in Bun + # Ref: https://github.com/oven-sh/bun/issues/15601 + - uses: actions/setup-node@v6 + with: + node-version: 24 + registry-url: https://registry.npmjs.org/ - uses: oven-sh/setup-bun@v2 with: bun-version: latest diff --git a/docs/docs_app/examples.py b/docs/docs_app/examples.py index a71a0b111..be1b6c21d 100644 --- a/docs/docs_app/examples.py +++ b/docs/docs_app/examples.py @@ -1,10 +1,9 @@ from __future__ import annotations -from collections.abc import Iterator +from collections.abc import Callable, Iterator from io import StringIO from pathlib import Path from traceback import format_exc -from typing import Callable import reactpy from reactpy.types import ComponentType @@ -154,7 +153,7 @@ def getvalue(self) -> str: def write(self, text: str) -> None: if len(self._lines) == self._max_lines: - self._lines = self._lines[1:] + (text,) + self._lines = (*self._lines[1:], text) else: self._lines += (text,) if self._callback is not None: diff --git a/docs/docs_app/prod.py b/docs/docs_app/prod.py index 0acf12432..0c10698f2 100644 --- a/docs/docs_app/prod.py +++ b/docs/docs_app/prod.py @@ -8,7 +8,7 @@ def main() -> None: app.run( host="0.0.0.0", # noqa: S104 - port=int(os.environ.get("PORT", 5000)), - workers=int(os.environ.get("WEB_CONCURRENCY", 1)), + port=int(os.environ.get("PORT", "5000")), + workers=int(os.environ.get("WEB_CONCURRENCY", "1")), debug=bool(int(os.environ.get("DEBUG", "0"))), ) diff --git a/docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/list_replace.py b/docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/list_replace.py index 4952b9597..5577da5e1 100644 --- a/docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/list_replace.py +++ b/docs/source/guides/adding-interactivity/dangers-of-mutability/_examples/list_replace.py @@ -8,7 +8,7 @@ def CounterList(): def make_increment_click_handler(index): def handle_click(event): new_value = counters[index] + 1 - set_counters(counters[:index] + [new_value] + counters[index + 1 :]) + set_counters([*counters[:index], new_value, *counters[index + 1 :]]) return handle_click diff --git a/docs/source/reference/_examples/matplotlib_plot.py b/docs/source/reference/_examples/matplotlib_plot.py index 5c4d616fe..a70f173ff 100644 --- a/docs/source/reference/_examples/matplotlib_plot.py +++ b/docs/source/reference/_examples/matplotlib_plot.py @@ -26,7 +26,7 @@ def ExpandableNumberInputs(values, set_values): def set_value_at_index(event, index=i): new_value = float(event["target"]["value"] or 0) - set_values(values[:index] + [new_value] + values[index + 1 :]) + set_values([*values[:index], new_value, *values[index + 1 :]]) inputs.append(poly_coef_input(i + 1, set_value_at_index)) diff --git a/docs/source/reference/_examples/simple_dashboard.py b/docs/source/reference/_examples/simple_dashboard.py index 58a3e6fff..0d39ab9af 100644 --- a/docs/source/reference/_examples/simple_dashboard.py +++ b/docs/source/reference/_examples/simple_dashboard.py @@ -57,7 +57,7 @@ async def animate(): "x": last_data_point["x"] + 1, "y": last_data_point["y"] + random.gauss(mu.current, sigma.current), } - set_data(data[1:] + [next_data_point]) + set_data([*data[1:], next_data_point]) return VictoryLine( { diff --git a/docs/source/reference/_examples/snake_game.py b/docs/source/reference/_examples/snake_game.py index bb4bbb541..5b5afc778 100644 --- a/docs/source/reference/_examples/snake_game.py +++ b/docs/source/reference/_examples/snake_game.py @@ -68,7 +68,7 @@ def on_direction_change(event): if hasattr(Direction, event["key"]): maybe_new_direction = Direction[event["key"]].value direction_vector_sum = tuple( - map(sum, zip(last_direction, maybe_new_direction)) + map(sum, zip(last_direction, maybe_new_direction, strict=False)) ) if direction_vector_sum != (0, 0): direction.current = maybe_new_direction @@ -109,7 +109,7 @@ async def animate(): set_food() new_snake = [*snake, new_snake_head] else: - new_snake = snake[1:] + [new_snake_head] + new_snake = [*snake[1:], new_snake_head] set_snake(new_snake) diff --git a/pyproject.toml b/pyproject.toml index 18874aec1..dcbeed297 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -210,11 +210,15 @@ build_client = ['hatch run "src/build_scripts/build_js_client.py" {args}'] build_app = ['hatch run "src/build_scripts/build_js_app.py" {args}'] publish_event_to_object = [ 'hatch run javascript:build_event_to_object', - 'cd "src/js/packages/event-to-object" && bunx npm publish --provenance --access public', + # FIXME: using npm publish to fix lack of "Trusted Publishing" in Bun + # Ref: https://github.com/oven-sh/bun/issues/15601 + 'cd "src/js/packages/event-to-object" && bunx npm@11.8.0 publish --provenance --access public', ] publish_client = [ 'hatch run javascript:build_client', - 'cd "src/js/packages/@reactpy/client" && bunx npm publish --provenance --access public', + # FIXME: using npm publish to fix lack of "Trusted Publishing" in Bun + # Ref: https://github.com/oven-sh/bun/issues/15601 + 'cd "src/js/packages/@reactpy/client" && bunx npm@11.8.0 publish --provenance --access public', ] ######################### diff --git a/src/reactpy/__init__.py b/src/reactpy/__init__.py index 5d1c64d56..0356dd540 100644 --- a/src/reactpy/__init__.py +++ b/src/reactpy/__init__.py @@ -23,7 +23,7 @@ from reactpy.utils import Ref, reactpy_to_string, string_to_reactpy __author__ = "The Reactive Python Team" -__version__ = "2.0.0b7" +__version__ = "2.0.0b8" __all__ = [ "Ref", diff --git a/src/reactpy/reactjs/__init__.py b/src/reactpy/reactjs/__init__.py index f0fac3611..597ae50a8 100644 --- a/src/reactpy/reactjs/__init__.py +++ b/src/reactpy/reactjs/__init__.py @@ -110,7 +110,7 @@ def component_from_npm( resolve_imports: bool = ..., resolve_imports_depth: int = ..., version: str = "latest", - cdn: str = "https://esm.sh", + cdn: str = "https://esm.sh/v135", fallback: Any | None = ..., unmount_before_update: bool = ..., allow_children: bool = ..., @@ -124,7 +124,7 @@ def component_from_npm( resolve_imports: bool = ..., resolve_imports_depth: int = ..., version: str = "latest", - cdn: str = "https://esm.sh", + cdn: str = "https://esm.sh/v135", fallback: Any | None = ..., unmount_before_update: bool = ..., allow_children: bool = ..., @@ -137,7 +137,7 @@ def component_from_npm( resolve_imports: bool = False, resolve_imports_depth: int = 5, version: str = "latest", - cdn: str = "https://esm.sh", + cdn: str = "https://esm.sh/v135", fallback: Any | None = None, unmount_before_update: bool = False, allow_children: bool = True, diff --git a/src/reactpy/reactjs/module.py b/src/reactpy/reactjs/module.py index d9ce6758d..337b1ec01 100644 --- a/src/reactpy/reactjs/module.py +++ b/src/reactpy/reactjs/module.py @@ -228,10 +228,10 @@ def import_reactjs( {"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}" + "react": "https://esm.sh/v135/react@{version}{postfix}", + "react-dom": "https://esm.sh/v135/react-dom@{version}{postfix}", + "react-dom/client": "https://esm.sh/v135/react-dom@{version}/client{postfix}", + "react/jsx-runtime": "https://esm.sh/v135/react@{version}/jsx-runtime{postfix}" }} }}""".replace("\n", "").replace(" ", ""), ) @@ -244,10 +244,10 @@ def import_reactjs( {"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}" + "react": "https://esm.sh/v135/preact@{version}/compat{postfix}", + "react-dom": "https://esm.sh/v135/preact@{version}/compat{postfix}", + "react-dom/client": "https://esm.sh/v135/preact@{version}/compat/client{postfix}", + "react/jsx-runtime": "https://esm.sh/v135/preact@{version}/compat/jsx-runtime{postfix}" }} }}""".replace("\n", "").replace(" ", ""), ) diff --git a/tests/test_reactjs/js_fixtures/keys-properly-propagated.js b/tests/test_reactjs/js_fixtures/keys-properly-propagated.js index 8d700397e..c37fab454 100644 --- a/tests/test_reactjs/js_fixtures/keys-properly-propagated.js +++ b/tests/test_reactjs/js_fixtures/keys-properly-propagated.js @@ -1,12 +1,12 @@ -import React from "https://esm.sh/react@19.0" -import ReactDOM from "https://esm.sh/react-dom@19.0/client" -import GridLayout from "https://esm.sh/react-grid-layout@1.5.0"; +import React from "https://esm.sh/v135/react@19.0" +import ReactDOM from "https://esm.sh/v135/react-dom@19.0/client" +import GridLayout from "https://esm.sh/v135/react-grid-layout@1.5.0"; export {GridLayout}; export function bind(node, config) { const root = ReactDOM.createRoot(node); return { - create: (type, props, children) => + create: (type, props, children) => React.createElement(type, props, children), render: (element) => root.render(element, node), unmount: () => root.unmount() diff --git a/tests/test_reactjs/js_fixtures/nest-custom-under-web.js b/tests/test_reactjs/js_fixtures/nest-custom-under-web.js index 027c9a2b7..7718e4f3e 100644 --- a/tests/test_reactjs/js_fixtures/nest-custom-under-web.js +++ b/tests/test_reactjs/js_fixtures/nest-custom-under-web.js @@ -1,14 +1,14 @@ -import React from "https://esm.sh/react@19.0" -import ReactDOM from "https://esm.sh/react-dom@19.0/client" -import {Container} from "https://esm.sh/react-bootstrap@2.10.10/?deps=react@19.0,react-dom@19.0,react-is@19.0&exports=Container"; -export {Container}; - -export function bind(node, config) { - const root = ReactDOM.createRoot(node); - return { - create: (type, props, children) => - React.createElement(type, props, children), - render: (element) => root.render(element, node), - unmount: () => root.unmount() - }; -} \ No newline at end of file +import React from "https://esm.sh/v135/react@19.0" +import ReactDOM from "https://esm.sh/v135/react-dom@19.0/client" +import {Container} from "https://esm.sh/v135/react-bootstrap@2.10.10/?deps=react@19.0,react-dom@19.0,react-is@19.0&exports=Container"; +export {Container}; + +export function bind(node, config) { + const root = ReactDOM.createRoot(node); + return { + create: (type, props, children) => + React.createElement(type, props, children), + render: (element) => root.render(element, node), + unmount: () => root.unmount() + }; +} diff --git a/tests/test_reactjs/test_modules.py b/tests/test_reactjs/test_modules.py index 7a458defa..9094310a1 100644 --- a/tests/test_reactjs/test_modules.py +++ b/tests/test_reactjs/test_modules.py @@ -103,7 +103,7 @@ async def test_nest_custom_component_under_web_component(display: DisplayFixture be nested under web components. """ Container = reactpy.reactjs.component_from_file( - JS_FIXTURES_DIR / "nest-custom-under-web.js", "Container", name="nest-custom-under-web" + JS_FIXTURES_DIR / "nest-custom-under-web.js", "Container" ) @reactpy.component diff --git a/tests/test_reactjs/test_modules_from_npm.py b/tests/test_reactjs/test_modules_from_npm.py index 2a7573955..4216ef672 100644 --- a/tests/test_reactjs/test_modules_from_npm.py +++ b/tests/test_reactjs/test_modules_from_npm.py @@ -406,7 +406,7 @@ def _get_chakra_components(): from reactpy.reactjs import component_from_string wrapper_js = """ - import { ChakraProvider as _ChakraProvider, defaultSystem, Box as _Box, Button as _Button } from "https://esm.sh/@chakra-ui/react@3?external=react,react-dom,react/jsx-runtime&bundle&target=es2020"; + import { ChakraProvider as _ChakraProvider, defaultSystem, Box as _Box, Button as _Button } from "https://esm.sh/v135/@chakra-ui/react@3?external=react,react-dom,react/jsx-runtime&bundle&target=es2020"; import * as React from "react"; export function ChakraProvider(props) { diff --git a/tests/test_web/js_fixtures/keys-properly-propagated.js b/tests/test_web/js_fixtures/keys-properly-propagated.js index 8d700397e..59e6ff96c 100644 --- a/tests/test_web/js_fixtures/keys-properly-propagated.js +++ b/tests/test_web/js_fixtures/keys-properly-propagated.js @@ -1,12 +1,12 @@ -import React from "https://esm.sh/react@19.0" -import ReactDOM from "https://esm.sh/react-dom@19.0/client" -import GridLayout from "https://esm.sh/react-grid-layout@1.5.0"; +import React from "https://esm.sh/v135/react@18.3.1" +import ReactDOM from "https://esm.sh/v135/react-dom@18.3.1/client" +import GridLayout from "https://esm.sh/v135/react-grid-layout@1.5.0"; export {GridLayout}; export function bind(node, config) { const root = ReactDOM.createRoot(node); return { - create: (type, props, children) => + create: (type, props, children) => React.createElement(type, props, children), render: (element) => root.render(element, node), unmount: () => root.unmount() diff --git a/tests/test_web/test_module.py b/tests/test_web/test_module.py index 34d857b7f..9b5cb87d9 100644 --- a/tests/test_web/test_module.py +++ b/tests/test_web/test_module.py @@ -2,6 +2,7 @@ THESE ARE TESTS FOR THE LEGACY API. SEE tests/test_reactjs/* FOR THE NEW API TESTS. THE CONTENTS OF THIS MODULE WILL BE MIGRATED OR DELETED ONCE THE LEGACY API IS REMOVED. """ + from pathlib import Path import pytest diff --git a/tests/test_widgets.py b/tests/test_widgets.py index fd44bd9f4..e734b91fb 100644 --- a/tests/test_widgets.py +++ b/tests/test_widgets.py @@ -1,8 +1,10 @@ from base64 import b64encode from pathlib import Path +import pytest + import reactpy -from reactpy.testing import DisplayFixture, poll +from reactpy.testing import GITHUB_ACTIONS, DisplayFixture, poll from tests.tooling.common import DEFAULT_TYPE_DELAY HERE = Path(__file__).parent @@ -108,6 +110,7 @@ def SomeComponent(): await poll_value.until_equals(12) +@pytest.mark.flaky(reruns=10 if GITHUB_ACTIONS else 1) async def test_use_linked_inputs_ignore_empty(display: DisplayFixture): value = reactpy.Ref(None)