proper computed var change detection
This commit is contained in:
parent
61e503c4d1
commit
8ceccc6140
@ -370,6 +370,9 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|||||||
# A special event handler for setting base vars.
|
# A special event handler for setting base vars.
|
||||||
setvar: ClassVar[EventHandler]
|
setvar: ClassVar[EventHandler]
|
||||||
|
|
||||||
|
# Track if computed vars have changed since last serialization
|
||||||
|
_changed_computed_vars: Set[str] = set()
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
parent_state: BaseState | None = None,
|
parent_state: BaseState | None = None,
|
||||||
@ -1825,14 +1828,11 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|||||||
calc_vars, dirty_vars = dirty_vars, set()
|
calc_vars, dirty_vars = dirty_vars, set()
|
||||||
for cvar in self._dirty_computed_vars(from_vars=calc_vars):
|
for cvar in self._dirty_computed_vars(from_vars=calc_vars):
|
||||||
actual_var = self.computed_vars.get(cvar)
|
actual_var = self.computed_vars.get(cvar)
|
||||||
if actual_var is not None:
|
assert actual_var is not None
|
||||||
if actual_var.has_changed(instance=self):
|
if actual_var.has_changed(instance=self):
|
||||||
actual_var.mark_dirty(instance=self)
|
actual_var.mark_dirty(instance=self)
|
||||||
else:
|
self.dirty_vars.add(cvar)
|
||||||
# var has not changed, do not mark as dirty
|
dirty_vars.add(cvar)
|
||||||
continue
|
|
||||||
self.dirty_vars.add(cvar)
|
|
||||||
dirty_vars.add(cvar)
|
|
||||||
|
|
||||||
def _expired_computed_vars(self) -> set[str]:
|
def _expired_computed_vars(self) -> set[str]:
|
||||||
"""Determine ComputedVars that need to be recalculated based on the expiration time.
|
"""Determine ComputedVars that need to be recalculated based on the expiration time.
|
||||||
@ -2112,6 +2112,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|||||||
state["__dict__"]["parent_state"] = None
|
state["__dict__"]["parent_state"] = None
|
||||||
state["__dict__"]["substates"] = {}
|
state["__dict__"]["substates"] = {}
|
||||||
state["__dict__"].pop("_was_touched", None)
|
state["__dict__"].pop("_was_touched", None)
|
||||||
|
state["__dict__"].pop("_changed_computed_vars", None)
|
||||||
# Remove all inherited vars.
|
# Remove all inherited vars.
|
||||||
for inherited_var_name in self.inherited_vars:
|
for inherited_var_name in self.inherited_vars:
|
||||||
state["__dict__"].pop(inherited_var_name, None)
|
state["__dict__"].pop(inherited_var_name, None)
|
||||||
|
@ -122,6 +122,7 @@ RESERVED_BACKEND_VAR_NAMES = {
|
|||||||
"_abc_impl",
|
"_abc_impl",
|
||||||
"_backend_vars",
|
"_backend_vars",
|
||||||
"_was_touched",
|
"_was_touched",
|
||||||
|
"_changed_computed_vars",
|
||||||
}
|
}
|
||||||
|
|
||||||
if sys.version_info >= (3, 11):
|
if sys.version_info >= (3, 11):
|
||||||
|
@ -2027,8 +2027,16 @@ class ComputedVar(Var[RETURN_TYPE]):
|
|||||||
else:
|
else:
|
||||||
# handle caching
|
# handle caching
|
||||||
if not hasattr(instance, self._cache_attr) or self.needs_update(instance):
|
if not hasattr(instance, self._cache_attr) or self.needs_update(instance):
|
||||||
|
# Get the new value.
|
||||||
|
new_value = self.fget(instance)
|
||||||
|
# Get the current cached value.
|
||||||
|
cached_value = getattr(instance, self._cache_attr, None)
|
||||||
|
# Check if the new value is different from the cached value.
|
||||||
|
if new_value == cached_value:
|
||||||
|
return new_value
|
||||||
|
instance._changed_computed_vars.add(self._js_expr)
|
||||||
# Set cache attr on state instance.
|
# Set cache attr on state instance.
|
||||||
setattr(instance, self._cache_attr, self.fget(instance))
|
setattr(instance, self._cache_attr, new_value)
|
||||||
# Ensure the computed var gets serialized to redis.
|
# Ensure the computed var gets serialized to redis.
|
||||||
instance._was_touched = True
|
instance._was_touched = True
|
||||||
# Set the last updated timestamp on the state instance.
|
# Set the last updated timestamp on the state instance.
|
||||||
@ -2176,9 +2184,19 @@ class ComputedVar(Var[RETURN_TYPE]):
|
|||||||
Returns:
|
Returns:
|
||||||
True if the value has changed, False otherwise.
|
True if the value has changed, False otherwise.
|
||||||
"""
|
"""
|
||||||
cached_value = getattr(instance, self._cache_attr, None)
|
if not self._cache:
|
||||||
|
return True
|
||||||
|
if self._js_expr in instance._changed_computed_vars:
|
||||||
|
return True
|
||||||
|
if not hasattr(instance, self._cache_attr):
|
||||||
|
return True
|
||||||
|
cached_value = getattr(instance, self._cache_attr)
|
||||||
new_value = self.fget(instance)
|
new_value = self.fget(instance)
|
||||||
return cached_value != new_value
|
has_changed = cached_value != new_value
|
||||||
|
if has_changed:
|
||||||
|
instance._changed_computed_vars.add(self._js_expr)
|
||||||
|
setattr(instance, self._cache_attr, new_value)
|
||||||
|
return has_changed
|
||||||
|
|
||||||
def _determine_var_type(self) -> Type:
|
def _determine_var_type(self) -> Type:
|
||||||
"""Get the type of the var.
|
"""Get the type of the var.
|
||||||
|
Loading…
Reference in New Issue
Block a user