Skip to content

Commit

Permalink
Track computed var dependency and substate dependency per-class
Browse files Browse the repository at this point in the history
There is no need to recompute these every time a state is initialized, which
can be basically for every request if we're using redis.
  • Loading branch information
masenf committed Nov 22, 2023
1 parent eb63f59 commit bd74a23
Showing 1 changed file with 32 additions and 25 deletions.
57 changes: 32 additions & 25 deletions reflex/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,10 +175,10 @@ class State(Base, ABC, extra=pydantic.Extra.allow):
event_handlers: ClassVar[Dict[str, EventHandler]] = {}

# Mapping of var name to set of computed variables that depend on it
_computed_var_dependencies: Dict[str, Set[str]] = {}
_computed_var_dependencies: ClassVar[Dict[str, Set[str]]] = {}

# Mapping of var name to set of substates that depend on it
_substate_var_dependencies: Dict[str, Set[str]] = {}
_substate_var_dependencies: ClassVar[Dict[str, Set[str]]] = {}

# The parent state.
parent_state: Optional[State] = None
Expand Down Expand Up @@ -218,10 +218,6 @@ def __init__(self, *args, parent_state: State | None = None, **kwargs):
kwargs["parent_state"] = parent_state
super().__init__(*args, **kwargs)

# initialize per-instance var dependency tracking
self._computed_var_dependencies = defaultdict(set)
self._substate_var_dependencies = defaultdict(set)

# Setup the substates.
for substate in self.get_substates():
substate_name = substate.get_name()
Expand All @@ -234,25 +230,6 @@ def __init__(self, *args, parent_state: State | None = None, **kwargs):
# Convert the event handlers to functions.
self._init_event_handlers()

# Initialize computed vars dependencies.
inherited_vars = set(self.inherited_vars).union(
set(self.inherited_backend_vars),
)
for cvar_name, cvar in self.computed_vars.items():
# Add the dependencies.
for var in cvar._deps(objclass=type(self)):
self._computed_var_dependencies[var].add(cvar_name)
if var in inherited_vars:
# track that this substate depends on its parent for this var
state_name = self.get_name()
parent_state = self.parent_state
while parent_state is not None and var in parent_state.vars:
parent_state._substate_var_dependencies[var].add(state_name)
state_name, parent_state = (
parent_state.get_name(),
parent_state.parent_state,
)

# Create a fresh copy of the backend variables for this instance
self._backend_vars = copy.deepcopy(self.backend_vars)

Expand Down Expand Up @@ -354,6 +331,33 @@ def __init_subclass__(cls, **kwargs):
cls.event_handlers[name] = handler
setattr(cls, name, handler)

cls._init_var_dependency_dicts()

@classmethod
def _init_var_dependency_dicts(cls):
# Initialize per-class var dependency tracking.
cls._computed_var_dependencies = defaultdict(set)
cls._substate_var_dependencies = defaultdict(set)

# Initialize computed vars dependencies.
inherited_vars = set(cls.inherited_vars).union(
set(cls.inherited_backend_vars),
)
for cvar_name, cvar in cls.computed_vars.items():
# Add the dependencies.
for var in cvar._deps(objclass=cls):
cls._computed_var_dependencies[var].add(cvar_name)
if var in inherited_vars:
# track that this substate depends on its parent for this var
state_name = cls.get_name()
parent_state = cls.get_parent_state()
while parent_state is not None and var in parent_state.vars:
parent_state._substate_var_dependencies[var].add(state_name)
state_name, parent_state = (
parent_state.get_name(),
parent_state.get_parent_state(),
)

@classmethod
def _check_overridden_methods(cls):
"""Check for shadow methods and raise error if any.
Expand Down Expand Up @@ -548,6 +552,9 @@ def add_var(cls, name: str, type_: Any, default_value: Any = None):
for substate_class in cls.__subclasses__():
substate_class.vars.setdefault(name, var)

# Reinitialize dependency tracking dicts
cls._init_var_dependency_dicts()

@classmethod
def _set_var(cls, prop: BaseVar):
"""Set the var as a class member.
Expand Down

0 comments on commit bd74a23

Please sign in to comment.