Fix get_var_value silently returning wrong values for operation vars#6633
Conversation
get_var_value resolved a var's state/field_name via _get_all_var_data(), which recursively merges the var-data of every constituent var. For an operation/derived var (e.g. State.a + State.b or State.items[0]) the merge back-filled state/field_name from the first operand, so get_var_value returned that operand's value instead of the operation's result, silently and without error. Resolve via the underlying var's own _var_data instead: unwrap any cast (ToOperation) wrappers and require the inner var to carry both state and field_name (a plain field or computed-var reference). Operation/derived vars now raise UnretrievableVarValueError rather than returning a plausible-but-wrong value, matching the documented behavior. Fixes #6629
Greptile SummaryThis PR fixes a silent correctness bug in
Confidence Score: 5/5Safe to merge — the change narrows a single code path in get_var_value to reject operation vars rather than silently mis-resolving them, and is guarded by new regression tests. The fix is minimal and surgical: it replaces a recursive var-data merge (which back-filled state/field_name from the first operand) with a direct check on the inner var's own _var_data after unwrapping ToOperation wrappers. Plain fields, object vars, and computed vars are unaffected. Operation vars now raise UnretrievableVarValueError instead of returning wrong values. The regression tests cover all three reported cases and confirm computed vars still resolve correctly. No existing functionality is removed or weakened. No files require special attention. Important Files Changed
Reviews (2): Last reviewed commit: "Add changelog fragment for #6633" | Re-trigger Greptile |
Merging this PR will not alter performance
Comparing Footnotes
|
TestState.array is a plain list[float] annotation, so pyright statically infers array[0] as float rather than a Var. Annotate the line with a justified pyright ignore (the expression is a Var operation at runtime).
Problem
State.get_var_value(var)returned an incorrect value, silently (no error), whenvarwas the result of a Var operation — an arithmetic/concatenation expression, or an indexed/attribute access on a state var. Instead of evaluating the operation, it returned the value of the operation's first constituent state field.Root cause
get_var_valueread the var'sstate/field_namefromvar._get_all_var_data(), which recursively merges the var-data of every constituent var. For an operation var the direct var-data has nostate/field_name, but the recursive merge back-fills them from the first operand (VarData.mergepicks the first non-emptystate/field_name). The guard therefore passed andgetattr(state, field_name)read that operand's field rather than the operation's result.Fix
Resolve via the underlying var's own var-data instead of the recursive merge:
ToOperation) wrappers — a plain field reference such asState.ais itself aNumberCastedVar(ToOperation)whose binding lives on._original.stateandfield_nameon its own_var_data(a plain field or computed-var reference).UnretrievableVarValueError, matching the method's documented behavior for vars with no associated state.A fuller fix could actually evaluate resolvable operations, but the framework has no server-side JS evaluator; at minimum it must not silently return a wrong value.
Behavior after the fix
State.a(field)11State.items(field)[10, 20, 30][10, 20, 30]State.mapping(object){...}{...}State.a + State.b1(wrong)UnretrievableVarValueErrorState.items[0][10, 20, 30](wrong)UnretrievableVarValueErrorTests
Extended
test_get_var_valuewith regression assertions: arithmetic, indexed, and item-access vars now raiseUnretrievableVarValueError, while plain fields, object vars, and computed vars still resolve. Verified the new assertions fail on the pre-fix code (DID NOT RAISE) and pass with the fix. Fulltests/units/test_state.py+tests/units/vars/test_dep_tracking.pypass;ruffandpyrightare clean.Fixes #6629
https://claude.ai/code/session_01BdVrRRM3VLwTWsfWWFX86U
Generated by Claude Code