Skip to content

Commit 06bfedd

Browse files
committed
Make the live event proxy opt-in
With live access enabled by default every dispatched object was registered with the client, which made even plain synchronous handlers block on the browser. Those handlers are therefore run in a worker thread, where calling set_state() fails with 'no running event loop' because scheduling a render needs the layout's loop. Default the option to off so existing apps keep the plain snapshot behaviour, and enable live access per handler or globally.
1 parent 117747c commit 06bfedd

4 files changed

Lines changed: 15 additions & 8 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ Don't forget to remove deprecated code on each major release!
4141
- `debounce` waits until activity stops, then fires once. Default is 200 ms on `input`/`select`/`textarea`, 0 ms elsewhere.
4242
- `throttle` caps the rate how often an event is allowed to execute. No default; opt in per event.
4343
- Added a live event proxy which lets event handlers read properties from, and call methods on, the browser objects which triggered them rather than being limited to the snapshot sent along with the event. Handlers can `await event.target.value` or `await event.target.focus()` when asynchronous, or simply use `event.target.value` and `event.target.focus()` when synchronous. The new `reactpy.types.AsyncEvent` and `reactpy.types.SyncEvent` types describe these objects for editors and type checkers.
44-
- This can be disabled entirely via `reactpy.config.REACTPY_ENABLE_EVENT_PROXY`.
44+
- This is opt-in. Enable it globally with `reactpy.config.REACTPY_ENABLE_EVENT_PROXY`, or per handler with `EventHandler(fn, event_proxy=True)`.
4545
- Unanswered requests are bounded by `reactpy.config.REACTPY_EVENT_PROXY_TIMEOUT`, `reactpy.config.REACTPY_EVENT_PROXY_MAX_TIMEOUTS`, and `reactpy.config.REACTPY_EVENT_PROXY_MAX_DEPTH`.
4646

4747
### Changed

‎src/reactpy/config.py‎

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,18 +141,23 @@ def boolean(value: str | bool | int) -> bool:
141141

142142
REACTPY_ENABLE_EVENT_PROXY = Option(
143143
"REACTPY_ENABLE_EVENT_PROXY",
144-
default=True,
144+
default=False,
145145
mutable=True,
146146
validator=boolean,
147147
)
148148
"""Whether event handlers receive live client-backed event objects.
149149
150-
When enabled (the default) the client keeps a reference to the dispatched JavaScript
151-
``Event`` around and lets the server read properties from, and call methods on, that
152-
object. Event handlers then receive a :class:`reactpy.types.SyncEvent` or
150+
Disabled by default. When enabled the client keeps a reference to the dispatched
151+
JavaScript ``Event`` around and lets the server read properties from, and call methods
152+
on, that object. Event handlers then receive a :class:`reactpy.types.SyncEvent` or
153153
:class:`reactpy.types.AsyncEvent` (both are ``dict`` subclasses holding the usual
154154
snapshot). When disabled, or when the client does not support live access, handlers
155155
receive the plain :class:`reactpy.types.Event` snapshot as they always have.
156+
157+
Individual handlers can opt in with ``EventHandler(fn, event_proxy=True)`` without
158+
turning this on globally. Note that a *synchronous* handler which reads a live value
159+
is run in a worker thread so that it may block, and cannot safely call ``set_state``
160+
from there yet.
156161
"""
157162

158163
REACTPY_EVENT_PROXY_TIMEOUT = Option(

‎tests/test_core/test_event_proxy.py‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
serialize_argument,
3232
)
3333
from reactpy.config import (
34+
REACTPY_ENABLE_EVENT_PROXY,
3435
REACTPY_EVENT_PROXY_MAX_TIMEOUTS,
3536
REACTPY_EVENT_PROXY_TIMEOUT,
3637
)
@@ -450,7 +451,8 @@ def event_handlers_of(model: dict[str, Any]) -> dict[str, Any]:
450451
raise KeyError(msg)
451452

452453

453-
async def test_layout_hands_handlers_events_of_their_own_flavour():
454+
async def test_layout_hands_handlers_events_of_their_own_flavour(monkeypatch):
455+
monkeypatch.setattr(REACTPY_ENABLE_EVENT_PROXY, "current", True)
454456
main_thread = threading.current_thread().name
455457
received: dict[str, tuple[Any, str, Any]] = {}
456458
objects = make_objects()
@@ -533,7 +535,8 @@ def MyComponent():
533535
assert client.requests == []
534536

535537

536-
async def test_merged_handlers_get_the_flavour_each_needs():
538+
async def test_merged_handlers_get_the_flavour_each_needs(monkeypatch):
539+
monkeypatch.setattr(REACTPY_ENABLE_EVENT_PROXY, "current", True)
537540
received: dict[str, Any] = {}
538541

539542
def sync_handler(args):

‎tests/test_core/test_serve.py‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ def make_events_and_expected_model():
6060
"target": STATIC_EVENT_HANDLER.target,
6161
"preventDefault": False,
6262
"stopPropagation": False,
63-
"eventProxy": True,
6463
}
6564
},
6665
}

0 commit comments

Comments
 (0)