Skip to content

Commit 8357d5a

Browse files
authored
ENG-6769: Fix module app.app not found during on_load (#5666)
* ENG-6769: Fix module app.app not found during on_load * py3.10 compat and extra safety for ClassVar[Any] * Fixup test_app.py for py310
1 parent 98a4f7c commit 8357d5a

File tree

3 files changed

+27
-4
lines changed

3 files changed

+27
-4
lines changed

reflex/state.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2463,16 +2463,30 @@ class OnLoadInternalState(State):
24632463
This is a separate substate to avoid deserializing the entire state tree for every page navigation.
24642464
"""
24652465

2466+
# Cannot properly annotate this as `App` due to circular import issues.
2467+
_app_ref: ClassVar[Any] = None
2468+
24662469
def on_load_internal(self) -> list[Event | EventSpec | event.EventCallback] | None:
24672470
"""Queue on_load handlers for the current page.
24682471
24692472
Returns:
24702473
The list of events to queue for on load handling.
2474+
2475+
Raises:
2476+
TypeError: If the app reference is not of type App.
24712477
"""
2472-
# Do not app._compile()! It should be already compiled by now.
2473-
load_events = prerequisites.get_and_validate_app().app.get_load_events(
2474-
self.router._page.path
2475-
)
2478+
from reflex.app import App
2479+
2480+
app = type(self)._app_ref or prerequisites.get_and_validate_app().app
2481+
if not isinstance(app, App):
2482+
msg = (
2483+
f"Expected app to be of type {App.__name__}, got {type(app).__name__}."
2484+
)
2485+
raise TypeError(msg)
2486+
# Cache the app reference for subsequent calls.
2487+
if type(self)._app_ref is None:
2488+
type(self)._app_ref = app
2489+
load_events = app.get_load_events(self.router._page.path)
24762490
if not load_events:
24772491
self.is_hydrated = True
24782492
return None # Fast path for navigation with no on_load events defined.
@@ -2646,6 +2660,9 @@ def reload_state_module(
26462660
state: Recursive argument for the state class to reload.
26472661
26482662
"""
2663+
# Reset the _app_ref of OnLoadInternalState to avoid stale references.
2664+
if state is OnLoadInternalState:
2665+
state._app_ref = None
26492666
# Clean out all potentially dirty states of reloaded modules.
26502667
for pd_state in tuple(state._potentially_dirty_states):
26512668
with contextlib.suppress(ValueError):

tests/units/test_app.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from collections.abc import Generator
88
from contextlib import nullcontext as does_not_raise
99
from pathlib import Path
10+
from typing import Any, ClassVar
1011
from unittest.mock import AsyncMock
1112

1213
import pytest
@@ -945,6 +946,7 @@ class DynamicState(BaseState):
945946
is_hydrated: bool = False
946947
loaded: int = 0
947948
counter: int = 0
949+
_app_ref: ClassVar[Any] = None
948950

949951
@rx.event
950952
def on_load(self):
@@ -982,6 +984,7 @@ def test_dynamic_arg_shadow(
982984
app_module_mock: Mocked app module.
983985
mocker: pytest mocker object.
984986
"""
987+
DynamicState._app_ref = None
985988
arg_name = "counter"
986989
route = f"/test/[{arg_name}]"
987990
app = app_module_mock.app = App(_state=DynamicState)
@@ -1030,6 +1033,7 @@ async def test_dynamic_route_var_route_change_completed_on_load(
10301033
app_module_mock: Mocked app module.
10311034
mocker: pytest mocker object.
10321035
"""
1036+
DynamicState._app_ref = None
10331037
arg_name = "dynamic"
10341038
route = f"test/[{arg_name}]"
10351039
app = app_module_mock.app = App(_state=DynamicState)

tests/units/test_state.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2948,6 +2948,7 @@ async def test_preprocess(
29482948
expected: Expected delta.
29492949
mocker: pytest mock object.
29502950
"""
2951+
OnLoadInternalState._app_ref = None
29512952
mocker.patch(
29522953
"reflex.state.State.class_subclasses", {test_state, OnLoadInternalState}
29532954
)
@@ -3002,6 +3003,7 @@ async def test_preprocess_multiple_load_events(
30023003
token: A token.
30033004
mocker: pytest mock object.
30043005
"""
3006+
OnLoadInternalState._app_ref = None
30053007
mocker.patch(
30063008
"reflex.state.State.class_subclasses", {OnLoadState, OnLoadInternalState}
30073009
)

0 commit comments

Comments
 (0)