From 4f12d2e2690c73c04e5e9b2a45e3c263e1bcf10a Mon Sep 17 00:00:00 2001 From: Masen Furer Date: Mon, 4 Mar 2024 13:13:08 -0800 Subject: [PATCH] Fix infinite recursion when a substate named "state" has a computed var (#2778) * test_potentially_dirty_substates: when a state named State should be computed Catch a regression introduced in 0.4.3a1 * _potentially_dirty_substates: qualify substate name When looking up substate classes, ensure the qualified name is used to avoid issues with same-named substates. --- reflex/state.py | 5 ++--- tests/test_state.py | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/reflex/state.py b/reflex/state.py index 055561c16..20552249b 100644 --- a/reflex/state.py +++ b/reflex/state.py @@ -1505,14 +1505,13 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow): """ # _always_dirty_substates need to be fetched to recalc computed vars. fetch_substates = set( - cls.get_class_substate(tuple(substate_name.split("."))) + cls.get_class_substate((cls.get_name(), *substate_name.split("."))) for substate_name in cls._always_dirty_substates ) - # Substates with cached vars also need to be fetched. for dependent_substates in cls._substate_var_dependencies.values(): fetch_substates.update( set( - cls.get_class_substate(tuple(substate_name.split("."))) + cls.get_class_substate((cls.get_name(), *substate_name.split("."))) for substate_name in dependent_substates ) ) diff --git a/tests/test_state.py b/tests/test_state.py index 3e97c82db..ea06dc0bb 100644 --- a/tests/test_state.py +++ b/tests/test_state.py @@ -2719,3 +2719,28 @@ async def test_get_state(mock_app: rx.App, token: str): "computed": "", }, } + + +# Save a reference to the rx.State to shadow the name State for testing. +RxState = State + + +def test_potentially_dirty_substates(): + """Test that potentially_dirty_substates returns the correct substates. + + Even if the name "State" is shadowed, it should still work correctly. + """ + + class State(RxState): + @ComputedVar + def foo(self) -> str: + return "" + + class C1(State): + @ComputedVar + def bar(self) -> str: + return "" + + assert RxState._potentially_dirty_substates() == {State} + assert State._potentially_dirty_substates() == {C1} + assert C1._potentially_dirty_substates() == set()