Skip to content

Use initial_value for computed var dependencies when building the initial state dict - #7905

Open
FarhanAliRaza wants to merge 2 commits into
mainfrom
claude/fix-5767-initial-value-dependency
Open

FarhanAliRaza wants to merge 2 commits into
mainfrom
claude/fix-5767-initial-value-dependency

Conversation

@FarhanAliRaza

@FarhanAliRaza FarhanAliRaza commented Nov 15, 2026 •

Copy link
Copy Markdown
Contributor

All Submissions:

  • Have you followed the guidelines stated in CONTRIBUTING.md file?
  • Have you checked to ensure there aren't any other open Pull Requests for the desired changed?

Type of change

  • Bug fix (non-breaking change which fixes an issue)

Changes To Core Features:

  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Have you written new tests for your core changes, as applicable?
  • Have you successfully ran tests with your changes locally?

Problem

BaseState.dict(initial=True) (used by compile_state and by _diff_against_initial_state) substitutes initial_value only for the computed vars it lists itself. A computed var without an initial value is still evaluated, and when its getter reads self.other_var, ComputedVar.__get__ runs other_var's getter even though it has an initial_value. With the repro from #5767 (return annotations added, which are required now), compile_state still raises var1: This should never happen at compile time on main.

Fix

  • reflex_base.vars.base gets a private ContextVar, _initial_dict_cache. BaseState.dict(initial=True) sets it to an empty dict around the evaluation of its computed vars and resets it in a finally.
  • 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 an initial_value yields that value and its getter does not run.
  • A cached var without an initial_value is 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_type runs on the values computed there, as it does at runtime.
  • The context var is set and reset inside one synchronous call, and a ContextVar is local to the task, so a live session never sees it: at runtime self.var1 runs the getter or uses its cache as before.
  • reflex now imports a symbol that only exists in unreleased reflex-base, so its floor is raised to reflex-base >= 0.11.1.dev0 (uv.lock is unchanged by this).

Deliberately not changed

  • Async computed vars: their coroutines are awaited later by _resolve_delta, outside dict(), so a read of a computed var from inside an async getter is not covered here.
  • The top-level substitution in dict(initial=True) and is_computed_var are left as they are.
  • No integration test: there is nothing to observe in a browser beyond what the initial dict contains.

Tests

tests/units/test_state.py::test_initial_dict_uses_initial_value_of_computed_var_dependencies builds the initial dict of a state and its substate and compares it in full: dependents of a cached and of an uncached var with initial_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 that dict() returns the runtime values (nothing from the initial pass was cached on the state). It fails on main (101/104/3 instead of 110/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 passed
  • uv run ruff check . — clean; uv run ruff format . — no changes
  • uv run pyright reflex/state.py packages/reflex-base/src/reflex_base/vars/base.py tests/units/test_state.py — 0 errors
  • The issue's repro through compile_state: raises without the fix, returns {'var1': True, 'var2': None} with it

https://claude.ai/code/session_01LwJUCUfMV8TRZsr6ehRyuR

closes #5767

…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
@FarhanAliRaza
FarhanAliRaza requested a review from a team as a code owner November 15, 2026 09:09
@greptile-apps

greptile-apps Bot commented Nov 15, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge.

Summary

Computed vars now use their dependencies’ initial_value while building the initial state, without saving those results for runtime use.

  • Initial state builds use initial values for computed-var 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

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@codspeed

codspeed Bot commented Nov 15, 2026 •

Copy link
Copy Markdown

Merging this PR will not alter performance

1 warning

⚠️ 14 benchmarks are dominated by system calls

System calls cannot be simulated consistently and are left out of the measure. The reported value understates the real cost. Switch to the Walltime instrument to measure them.

Learn more about system calls

See affected benchmarks

✅ 143 untouched benchmarks
⏩ 8 skipped benchmarks1


Comparing claude/fix-5767-initial-value-dependency (e115591) with main (8e5bb84)

Open in CodSpeed

Footnotes

  1. 8 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

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

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A computed var "B" that accesses another computed var "A" with initial_value causes "A" to be computed at compile time

1 participant