From f7822451b33cb2130872e2d1851680e1d1089996 Mon Sep 17 00:00:00 2001 From: Masen Furer Date: Thu, 26 Sep 2024 17:08:30 -0700 Subject: [PATCH 1/2] Track backend-only vars that are declared without a default value Without this provision, declared backend vars can be accidentally shared among all states if a mutable value is assigned to the class attribute. --- reflex/state.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/reflex/state.py b/reflex/state.py index c16b37b69c..6af70db14f 100644 --- a/reflex/state.py +++ b/reflex/state.py @@ -30,6 +30,7 @@ Type, Union, cast, + get_type_hints, ) import dill @@ -573,6 +574,15 @@ def __init_subclass__(cls, mixin: bool = False, **kwargs): for name, value in cls.__dict__.items() if types.is_backend_base_variable(name, cls) } + # Add annotated backend vars that do not have a default value. + new_backend_vars.update( + { + name: Var("", _var_type=annotation_value).get_default_value() + for name, annotation_value in get_type_hints(cls).items() + if name not in new_backend_vars + and types.is_backend_base_variable(name, cls) + } + ) cls.backend_vars = { **cls.inherited_backend_vars, From 55c07cf9603b44b8a169f4b1ebfc60d9add80db5 Mon Sep 17 00:00:00 2001 From: Masen Furer Date: Thu, 26 Sep 2024 17:28:14 -0700 Subject: [PATCH 2/2] add test case for no default backend var --- tests/units/test_state.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/units/test_state.py b/tests/units/test_state.py index 89aad15364..205162b9f2 100644 --- a/tests/units/test_state.py +++ b/tests/units/test_state.py @@ -3216,6 +3216,7 @@ class MixinState(State, mixin=True): num: int = 0 _backend: int = 0 + _backend_no_default: dict @rx.var(cache=True) def computed(self) -> str: @@ -3243,11 +3244,16 @@ def test_mixin_state() -> None: """Test that a mixin state works correctly.""" assert "num" in UsesMixinState.base_vars assert "num" in UsesMixinState.vars - assert UsesMixinState.backend_vars == {"_backend": 0} + assert UsesMixinState.backend_vars == {"_backend": 0, "_backend_no_default": {}} assert "computed" in UsesMixinState.computed_vars assert "computed" in UsesMixinState.vars + assert ( + UsesMixinState(_reflex_internal_init=True)._backend_no_default # type: ignore + is not UsesMixinState.backend_vars["_backend_no_default"] + ) + def test_child_mixin_state() -> None: """Test that mixin vars are only applied to the highest state in the hierarchy."""