From a2af3593b1116e904ffcd2447a968c9b089c92c9 Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Sat, 27 Jun 2026 16:48:40 -0700 Subject: [PATCH 1/5] misc hanging changes --- CHANGELOG.md | 2 +- src/build_scripts/install_deps.py | 44 +++++++++++++++++++++++++++++++ tests/test_core/test_events.py | 6 ++--- 3 files changed, 47 insertions(+), 5 deletions(-) create mode 100644 src/build_scripts/install_deps.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 09b81c68f..86dff1b89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,7 +60,7 @@ Don't forget to remove deprecated code on each major release! - `reactpy.core.vdom._CustomVdomDictConstructor` has been moved to `reactpy.types.CustomVdomConstructor`. - `reactpy.core.vdom._EllipsisRepr` has been moved to `reactpy.types.EllipsisRepr`. - `reactpy.types.VdomDictConstructor` has been renamed to `reactpy.types.VdomConstructor`. -- `REACTPY_ASYNC_RENDERING` can now de-duplicate and cascade renders where necessary. +- `REACTPY_ASYNC_RENDERING` can now de-duplicate renders where necessary. - `REACTPY_ASYNC_RENDERING` is now defaulted to `True` for up to 40x performance improvements in environments with high concurrency. - Events now support debounce, which can now be configured per event with `event.debounce = `. Note that `input`, `select`, and `textarea` elements default to 200ms debounce. diff --git a/src/build_scripts/install_deps.py b/src/build_scripts/install_deps.py new file mode 100644 index 000000000..48481d93f --- /dev/null +++ b/src/build_scripts/install_deps.py @@ -0,0 +1,44 @@ +""" +Development/debug script to parse pyproject.toml to find dependecies then install them in the local +environment via `uv pip install -U ` +""" + +import subprocess +import tomllib as toml +from pathlib import Path + +DEPENDENCIES = set() + + +def find_deps(data): + """Recurse through all categories and find any list with `dependencies` in the name, then combine + all dependencies into a single list""" + if isinstance(data, dict): + for key, value in data.items(): + if ( + "dependencies" in key + and isinstance(value, list) + and value + and isinstance(value[0], str) + ): + DEPENDENCIES.update(value) + else: + find_deps(value) + elif isinstance(data, list): + for item in data: + find_deps(item) + + +def install_deps(): + pyproject_path = Path(__file__).parent.parent / "pyproject.toml" + with open(pyproject_path, "rb") as f: + pyproject_data = toml.load(f) + find_deps(pyproject_data) + DEPENDENCIES.discard( + "ruff" + ) # ruff only exists in dev dependencies for CI purposes. + subprocess.run(["uv", "pip", "install", "-U", *DEPENDENCIES], check=False) # noqa: S607 + + +if __name__ == "__main__": + install_deps() diff --git a/tests/test_core/test_events.py b/tests/test_core/test_events.py index 71744d64b..4f96120f3 100644 --- a/tests/test_core/test_events.py +++ b/tests/test_core/test_events.py @@ -646,12 +646,10 @@ async def add_top(event): assert clicked_items == ["B"] -async def test_controlled_input_typing(display: DisplayFixture): +async def test_controlled_input_rapid_typing(display: DisplayFixture): """ Test that a controlled input updates correctly even with rapid typing. - This validates that user inputs are processed in the correct order and that the - event queueing/processing order is consistent with user expectations, even if the - server is still processing previous events. + This validates that user inputs are properly debounced by the client. """ @reactpy.component From ffce7b9a93c2233ce98aaaede02e661a4374ec5e Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Sun, 28 Jun 2026 00:37:21 -0700 Subject: [PATCH 2/5] debounce refactoring and add throttle --- .../@reactpy/client/src/components.tsx | 137 +++++++++- .../packages/@reactpy/client/src/handler.ts | 56 ++++ src/js/packages/@reactpy/client/src/index.ts | 1 + src/js/packages/@reactpy/client/src/types.ts | 1 + src/js/packages/@reactpy/client/src/vdom.tsx | 59 +++-- src/reactpy/core/_event_inspect.py | 246 ++++++++++++++++++ src/reactpy/core/events.py | 92 +++---- src/reactpy/core/layout.py | 42 +-- src/reactpy/core/vdom.py | 1 + src/reactpy/types.py | 33 ++- tests/test_core/test_events.py | 148 ++++++++++- tests/test_core/test_layout.py | 80 ++++++ 12 files changed, 771 insertions(+), 125 deletions(-) create mode 100644 src/js/packages/@reactpy/client/src/handler.ts create mode 100644 src/reactpy/core/_event_inspect.py diff --git a/src/js/packages/@reactpy/client/src/components.tsx b/src/js/packages/@reactpy/client/src/components.tsx index 2ce16d5fa..e51828574 100644 --- a/src/js/packages/@reactpy/client/src/components.tsx +++ b/src/js/packages/@reactpy/client/src/components.tsx @@ -8,6 +8,14 @@ import { type TargetedEvent, } from "preact"; import { useContext, useEffect, useRef, useState } from "preact/hooks"; +import { + HANDLER_DEBOUNCE, + HANDLER_MARKER, + HANDLER_THROTTLE, + getInitialDebounce, + isValidDebounce, + type TaggedEventHandler, +} from "./handler"; import type { ImportSourceBinding, ReactPyComponent, @@ -18,12 +26,25 @@ import type { ReactPyClient } from "./client"; const ClientContext = createContext(null as any); -const DEFAULT_INPUT_DEBOUNCE = 200; - -type ReactPyInputHandler = ((event: TargetedEvent) => void) & { - debounce?: number; - isHandler?: boolean; -}; +// Built-in default debounce (ms) used by user-input elements (````, +// ``