Raise a clear error when a hybrid property reads a backend var on a state#6621
Raise a clear error when a hybrid property reads a backend var on a state#6621masenf wants to merge 3 commits into
Conversation
Merging this PR will not alter performance
Comparing Footnotes
|
Greptile SummaryThis PR adds a guard that raises a clear
Confidence Score: 5/5Safe to merge — the guard is narrowly scoped to BaseState subclasses, backend_vars correctly includes inherited vars so the optimization path is sound, and no existing behavior is changed for states with no backend vars or for object-var access. The change is well-contained: a new private proxy class, one new exception, and a narrowly conditional branch in get. The core invariant — that backend_vars already aggregates inherited backend vars — is confirmed in the state metaclass. No breaking changes, and the test suite directly exercises the four meaningful cases. No files require special attention. Important Files Changed
Reviews (5): Last reviewed commit: "fix(hybrid_property): return new descrip..." | Re-trigger Greptile |
977c6c2 to
c44fc0a
Compare
c44fc0a to
bff3fa1
Compare
…on a state A hybrid property's frontend logic (its getter, or a custom @<name>.var function) runs with the state class as `self` when building the frontend var. Reading a backend (underscore-prefixed) var there previously baked the var's class-level default into the frontend as a frozen literal — a silent leak that never updates and is not reactive. HybridProperty.__get__ now wraps a state owner in a _StateBackendVarGuard while building the frontend var; reading a backend var raises HybridPropertyError, pointing at the misuse in the user's getter/.var function. Object-var owners (nested dataclass / pydantic / SQLAlchemy access) have no backend vars and are unaffected. The guard lives at the single point where state-ness is determined, so there is no redundant BaseState lookup. https://claude.ai/code/session_01DKFiYGnWRQG8wMNKFW7obm
bff3fa1 to
9c1894f
Compare
…in mutation HybridProperty.var() now constructs a new instance instead of mutating self, matching property.setter semantics. This prevents a shared mixin descriptor from being silently mutated when one concrete subclass calls .var — other subclasses that inherit the mixin no longer see a leaked _var. Also adds an early-return in __get__ when owner.backend_vars is empty, skipping the guard entirely when there is nothing to guard against.
Type of change
Note
Stacked on #6619 — base branch is
claude/relaxed-cerf-Z110q. This PR contains only the backend-var guard; review it against that base.Description
A hybrid property's frontend logic (its getter, or a custom
@<name>.varfunction) runs with the state class asselfwhen building the frontend var. Reading a backend (underscore-prefixed) var there previously baked the var's class-level default into the frontend as a frozen literal — a silent leak that never updates and is not reactive.This PR makes that misuse fail loudly with a clear, actionable error.
Changes:
HybridProperty._get_varguards the state owner (packages/reflex-base/src/reflex_base/vars/hybrid_property.py): when a hybrid property is resolved against aBaseState, the owner is wrapped in a_StateBackendVarGuard. Accessing a backend var while building the frontend var raises, with the traceback pointing at the offending line in the user's getter/.varfunction. Object-var owners (nested dataclass / pydantic / SQLAlchemy access) have no backend vars and are passed through unchanged.New
HybridPropertyError(packages/reflex-base/src/reflex_base/utils/exceptions.py): a dedicatedReflexError— deliberately not anAttributeError, so it can't be silently swallowed bygetattr(..., default)/hasattr— whose message names the property, the state, and the offending backend var, and suggests using a regular var or a separate@<name>.varimplementation.Tests
tests/units/vars/test_hybrid_property.py:HybridPropertyErrorfrom both the getter and a custom.varfunctionhttps://claude.ai/code/session_01DKFiYGnWRQG8wMNKFW7obm