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 1 commit into
Conversation
Merging this PR will not alter performance
Comparing Footnotes
|
Greptile SummaryThis PR adds a runtime guard that raises
Confidence Score: 5/5Safe to merge — the change is additive (raises where it previously silently misbehaved) and is well-scoped to the state-class code path. The guard logic is correct: No files require special attention. Important Files Changed
Reviews (3): Last reviewed commit: "feat: raise a clear error when a hybrid ..." | Re-trigger Greptile |
| class HybridProperty(property): | ||
| """A hybrid property that can also be used in frontend/as var.""" | ||
|
|
||
| # The optional var function for the property. | ||
| _var: Callable[[Any], Var] | None = None |
There was a problem hiding this comment.
_var class attribute is mutable via instance sharing through inheritance
_var is defined as a class attribute (None) and set as an instance attribute by var(). When a HybridProperty is defined on a mixin and multiple classes inherit it without overriding, all subclasses share the exact same descriptor object. Calling var() on that shared object mutates it for all inheritors simultaneously — a subclass cannot independently override just the .var() function of an inherited HybridProperty without redefining the whole property. A short note in the docstring would help users avoid the pitfall.
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
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