Skip to content

Commit c441e33

Browse files
authored
ENG-8664: MutableProxy.wrap_recursive fully rebinds existing proxy values (#6083)
This will convert ImmutableMutableProxy to MutableProxy for example when a value is stashed from a background event, then is accessed from a regular event.
1 parent ce9b21a commit c441e33

File tree

2 files changed

+15
-5
lines changed

2 files changed

+15
-5
lines changed

reflex/istate/proxy.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -509,11 +509,10 @@ def _wrap_recursive(self, value: Any) -> Any:
509509
# When called from dataclasses internal code, return the unwrapped value
510510
if self._is_called_from_dataclasses_internal():
511511
return value
512-
# If we already have a proxy, make sure the state reference is up to date and return it.
512+
# If we already have a proxy, unwrap and rewrap to make sure the state
513+
# reference is up to date.
513514
if isinstance(value, MutableProxy):
514-
if value._self_state is not self._self_state:
515-
value._self_state = self._self_state
516-
return value
515+
value = value.__wrapped__
517516
# Recursively wrap mutable types.
518517
if is_mutable_type(type(value)):
519518
base_cls = globals()[self.__base_proxy__]

tests/units/test_state.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
from reflex.istate.manager.redis import StateManagerRedis
3636
from reflex.state import (
3737
BaseState,
38+
ImmutableMutableProxy,
3839
ImmutableStateError,
3940
MutableProxy,
4041
OnLoadInternalState,
@@ -4419,20 +4420,30 @@ async def test_rebind_mutable_proxy(mock_app: rx.App, token: str) -> None:
44194420
"token": token,
44204421
"sid": "test_sid",
44214422
})
4423+
assert isinstance(state, MutableProxyState)
4424+
assert isinstance(state.data, MutableProxy)
4425+
assert not isinstance(state.data, ImmutableMutableProxy)
44224426
state_proxy = StateProxy(state)
4423-
assert isinstance(state_proxy.data, MutableProxy)
4427+
assert isinstance(state_proxy.data, ImmutableMutableProxy)
44244428
async with state_proxy:
4429+
# This assigns an ImmutableMutableProxy to data["a"].
44254430
state_proxy.data["a"] = state_proxy.data["b"]
4431+
assert isinstance(state_proxy.data["a"], ImmutableMutableProxy)
44264432
assert state_proxy.data["a"] is not state_proxy.data["b"]
44274433
assert state_proxy.data["a"].__wrapped__ is state_proxy.data["b"].__wrapped__
44284434

4435+
# Rebinding with a non-proxy should return a MutableProxy object (not ImmutableMutableProxy).
4436+
assert isinstance(state_proxy.__wrapped__.data["a"], MutableProxy)
4437+
assert not isinstance(state_proxy.__wrapped__.data["a"], ImmutableMutableProxy)
4438+
44294439
# Flush any oplock.
44304440
await mock_app.state_manager.close()
44314441

44324442
new_state_proxy = StateProxy(state)
44334443
assert state_proxy is not new_state_proxy
44344444
assert new_state_proxy.data["a"]._self_state is new_state_proxy
44354445
assert state_proxy.data["a"]._self_state is state_proxy
4446+
assert state_proxy.__wrapped__.data["a"]._self_state is state_proxy.__wrapped__
44364447

44374448
async with state_proxy:
44384449
state_proxy.data["a"].append(3)

0 commit comments

Comments
 (0)