Repository navigation
Use initial_value for computed var dependencies when building the initial state dict - #7905
FarhanAliRaza wants to merge 2 commits into
Conversation
…dict BaseState.dict(initial=True) substituted initial_value only for the computed vars it listed itself. A computed var without an initial value was still evaluated, and any computed var its getter read ran its own getter, so compiling or exporting an app failed when that getter needed a database or a request. While the initial dict is built, ComputedVar.__get__ now yields the initial value of a computed var that has one, and caches nothing derived from it. Regular reads are unchanged. Fixes #5767 Claude-Session: https://claude.ai/code/session_01LwJUCUfMV8TRZsr6ehRyuR
[Medium impact] The PR appears safe to merge. SummaryComputed vars now use their dependencies’
The previous repeated-getter finding is addressed. No new actionable issues were found. Reviews (2) · Last reviewed commit: "Compute cached computed vars once per in..." · Reviewed by Greptile |
There was a problem hiding this comment.
All reported issues were addressed across 6 files
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
Merging this PR will not alter performance1 warning
Comparing Footnotes
|
Values computed from initial values are kept in a cache that lives for one dict(initial=True) call, so a cached computed var read several times runs its getter once, as it does at runtime, and still never reaches the instance cache. The return type check runs on those values too. Claude-Session: https://claude.ai/code/session_01LwJUCUfMV8TRZsr6ehRyuR
All Submissions:
Type of change
Changes To Core Features:
Problem
BaseState.dict(initial=True)(used bycompile_stateand by_diff_against_initial_state) substitutesinitial_valueonly for the computed vars it lists itself. A computed var without an initial value is still evaluated, and when its getter readsself.other_var,ComputedVar.__get__runsother_var's getter even though it has aninitial_value. With the repro from #5767 (return annotations added, which are required now),compile_statestill raisesvar1: This should never happen at compile timeonmain.Fix
reflex_base.vars.basegets a privateContextVar,_initial_dict_cache.BaseState.dict(initial=True)sets it to an empty dict around the evaluation of its computed vars and resets it in afinally.ComputedVar.__get__checks it only where it is about to call the getter (uncached var, or cache miss), so a read that hits the cache costs nothing extra. While it is set, a var with aninitial_valueyields that value and its getter does not run.initial_valueis computed once per initial-dict build into that build-local cache, and never written to the instance cache, so repeated reads do not rerun the getter and nothing derived from initial values can be served later._check_deprecated_return_typeruns on the values computed there, as it does at runtime.ContextVaris local to the task, so a live session never sees it: at runtimeself.var1runs the getter or uses its cache as before.reflexnow imports a symbol that only exists in unreleasedreflex-base, so its floor is raised toreflex-base >= 0.11.1.dev0(uv.lockis unchanged by this).Deliberately not changed
_resolve_delta, outsidedict(), so a read of a computed var from inside anasyncgetter is not covered here.dict(initial=True)andis_computed_varare left as they are.Tests
tests/units/test_state.py::test_initial_dict_uses_initial_value_of_computed_var_dependenciesbuilds the initial dict of a state and its substate and compares it in full: dependents of a cached and of an uncached var withinitial_value, a transitive dependent, and a substate reading inherited vars all see the initial values. No getter of a var with an initial value is called, and the cached var without one, which is read twice, runs its getter once. It then reads the vars normally and checks that the getters run, in the exact order expected, and thatdict()returns the runtime values (nothing from the initial pass was cached on the state). It fails onmain(101/104/3instead of110/140/30).Run locally:
uv run pytest tests/units/test_state.py tests/units/vars tests/units/reflex_base/vars tests/units/compiler -q— 1216 passeduv run ruff check .— clean;uv run ruff format .— no changesuv run pyright reflex/state.py packages/reflex-base/src/reflex_base/vars/base.py tests/units/test_state.py— 0 errorscompile_state: raises without the fix, returns{'var1': True, 'var2': None}with ithttps://claude.ai/code/session_01LwJUCUfMV8TRZsr6ehRyuR
closes #5767