Improve event processing performance (#163)
This commit is contained in:
parent
429b21260d
commit
974d2b4cbb
@ -59,11 +59,11 @@ BUN_PATH = "$HOME/.bun/bin/bun"
|
||||
# Command to install bun.
|
||||
INSTALL_BUN = "curl https://bun.sh/install | bash"
|
||||
# Command to run the backend in dev mode.
|
||||
RUN_BACKEND = "uvicorn --log-level critical --reload --host 0.0.0.0".split()
|
||||
RUN_BACKEND = "uvicorn --log-level debug --reload --host 0.0.0.0".split()
|
||||
# The default timeout when launching the gunicorn server.
|
||||
TIMEOUT = 120
|
||||
# The command to run the backend in production mode.
|
||||
RUN_BACKEND_PROD = f"gunicorn --worker-class uvicorn.workers.UvicornH11Worker --bind 0.0.0.0:8000 --preload --timeout {TIMEOUT} --log-level debug".split()
|
||||
RUN_BACKEND_PROD = f"gunicorn --worker-class uvicorn.workers.UvicornH11Worker --bind 0.0.0.0:8000 --preload --timeout {TIMEOUT} --log-level critical".split()
|
||||
|
||||
# Compiler variables.
|
||||
# The extension for compiled Javascript files.
|
||||
|
@ -257,40 +257,6 @@ class State(Base, ABC):
|
||||
field.required = False
|
||||
field.default = default_value
|
||||
|
||||
def getattr(self, name: str) -> Any:
|
||||
"""Get a non-prop attribute.
|
||||
|
||||
Args:
|
||||
name: The name of the attribute.
|
||||
|
||||
Returns:
|
||||
The attribute.
|
||||
"""
|
||||
return super().__getattribute__(name)
|
||||
|
||||
def __getattribute__(self, name: str) -> Any:
|
||||
"""Get the attribute.
|
||||
|
||||
Args:
|
||||
name: The name of the attribute.
|
||||
|
||||
Returns:
|
||||
The attribute.
|
||||
|
||||
Raises:
|
||||
Exception: If the attribute is not found.
|
||||
"""
|
||||
# If it is an inherited var, return from the parent state.
|
||||
if name != "inherited_vars" and name in self.inherited_vars:
|
||||
return getattr(self.parent_state, name)
|
||||
try:
|
||||
return super().__getattribute__(name)
|
||||
except Exception as e:
|
||||
# Check if the attribute is a substate.
|
||||
if name in self.substates:
|
||||
return self.substates[name]
|
||||
raise e
|
||||
|
||||
def __setattr__(self, name: str, value: Any):
|
||||
"""Set the attribute.
|
||||
|
||||
@ -298,20 +264,13 @@ class State(Base, ABC):
|
||||
name: The name of the attribute.
|
||||
value: The value of the attribute.
|
||||
"""
|
||||
# NOTE: We use super().__getattribute__ for performance reasons.
|
||||
if name != "inherited_vars" and name in super().__getattribute__(
|
||||
"inherited_vars"
|
||||
):
|
||||
setattr(super().__getattribute__("parent_state"), name, value)
|
||||
return
|
||||
|
||||
# Set the attribute.
|
||||
super().__setattr__(name, value)
|
||||
|
||||
# Add the var to the dirty list.
|
||||
if name in super().__getattribute__("vars"):
|
||||
super().__getattribute__("dirty_vars").add(name)
|
||||
super().__getattribute__("mark_dirty")()
|
||||
if name in self.vars:
|
||||
self.dirty_vars.add(name)
|
||||
self.mark_dirty()
|
||||
|
||||
def reset(self):
|
||||
"""Reset all the base vars to their default values."""
|
||||
@ -358,11 +317,10 @@ class State(Base, ABC):
|
||||
Returns:
|
||||
The state update after processing the event.
|
||||
"""
|
||||
# NOTE: We use super().__getattribute__ for performance reasons.
|
||||
# Get the event handler.
|
||||
path = event.name.split(".")
|
||||
path, name = path[:-1], path[-1]
|
||||
substate = super().__getattribute__("get_substate")(path)
|
||||
substate = self.get_substate(path)
|
||||
handler = getattr(substate, name)
|
||||
|
||||
# Process the event.
|
||||
@ -383,10 +341,10 @@ class State(Base, ABC):
|
||||
events = utils.fix_events(events, event.token)
|
||||
|
||||
# Get the delta after processing the event.
|
||||
delta = super().__getattribute__("get_delta")()
|
||||
delta = self.get_delta()
|
||||
|
||||
# Reset the dirty vars.
|
||||
super().__getattribute__("clean")()
|
||||
self.clean()
|
||||
|
||||
# Return the state update.
|
||||
return StateUpdate(delta=delta, events=events)
|
||||
@ -397,22 +355,20 @@ class State(Base, ABC):
|
||||
Returns:
|
||||
The delta for the state.
|
||||
"""
|
||||
# NOTE: We use super().__getattribute__ for performance reasons.
|
||||
delta = {}
|
||||
|
||||
# Return the dirty vars, as well as all computed vars.
|
||||
subdelta = {
|
||||
prop: getattr(self, prop)
|
||||
for prop in super().__getattribute__("dirty_vars")
|
||||
| set(super().__getattribute__("computed_vars").keys())
|
||||
for prop in self.dirty_vars | self.computed_vars.keys()
|
||||
}
|
||||
if len(subdelta) > 0:
|
||||
delta[super().__getattribute__("get_full_name")()] = subdelta
|
||||
delta[self.get_full_name()] = subdelta
|
||||
|
||||
# Recursively find the substate deltas.
|
||||
substates = super().__getattribute__("substates")
|
||||
for substate in super().__getattribute__("dirty_substates"):
|
||||
delta.update(substates[substate].getattr("get_delta")())
|
||||
substates = self.substates
|
||||
for substate in self.dirty_substates:
|
||||
delta.update(substates[substate].get_delta())
|
||||
|
||||
# Format the delta.
|
||||
delta = utils.format_state(delta)
|
||||
@ -428,14 +384,13 @@ class State(Base, ABC):
|
||||
|
||||
def clean(self):
|
||||
"""Reset the dirty vars."""
|
||||
# NOTE: We use super().__getattribute__ for performance reasons.
|
||||
# Recursively clean the substates.
|
||||
for substate in super().__getattribute__("dirty_substates"):
|
||||
super().__getattribute__("substates")[substate].getattr("clean")()
|
||||
for substate in self.dirty_substates:
|
||||
self.substates[substate].clean()
|
||||
|
||||
# Clean this state.
|
||||
super().__setattr__("dirty_vars", set())
|
||||
super().__setattr__("dirty_substates", set())
|
||||
self.dirty_vars = set()
|
||||
self.dirty_substates = set()
|
||||
|
||||
def dict(self, include_computed: bool = True, **kwargs) -> Dict[str, Any]:
|
||||
"""Convert the object to a dictionary.
|
||||
|
@ -28,9 +28,9 @@ from typing import (
|
||||
Type,
|
||||
Union,
|
||||
)
|
||||
import typer
|
||||
|
||||
import plotly.graph_objects as go
|
||||
import typer
|
||||
from plotly.io import to_json
|
||||
from redis import Redis
|
||||
from rich.console import Console
|
||||
|
@ -124,7 +124,7 @@ def test_initialize_with_state(TestState: Type[State]):
|
||||
token = "token"
|
||||
state = app.get_state(token)
|
||||
assert isinstance(state, TestState)
|
||||
assert state.var == 0
|
||||
assert state.var == 0 # type: ignore
|
||||
|
||||
|
||||
def test_set_and_get_state(TestState: Type[State]):
|
||||
@ -142,8 +142,8 @@ def test_set_and_get_state(TestState: Type[State]):
|
||||
# Get the default state for each token.
|
||||
state1 = app.get_state(token1)
|
||||
state2 = app.get_state(token2)
|
||||
assert state1.var == 0
|
||||
assert state2.var == 0
|
||||
assert state1.var == 0 # type: ignore
|
||||
assert state2.var == 0 # type: ignore
|
||||
|
||||
# Set the vars to different values.
|
||||
state1.var = 1
|
||||
@ -154,5 +154,5 @@ def test_set_and_get_state(TestState: Type[State]):
|
||||
# Get the states again and check the values.
|
||||
state1 = app.get_state(token1)
|
||||
state2 = app.get_state(token2)
|
||||
assert state1.var == 1
|
||||
assert state2.var == 2
|
||||
assert state1.var == 1 # type: ignore
|
||||
assert state2.var == 2 # type: ignore
|
||||
|
@ -411,12 +411,15 @@ def test_get_child_attribute(TestState, ChildState, ChildState2, GrandchildState
|
||||
GrandchildState: The grandchild state class.
|
||||
"""
|
||||
test_state = TestState()
|
||||
child_state = test_state.get_substate(["child_state"])
|
||||
child_state2 = test_state.get_substate(["child_state2"])
|
||||
grandchild_state = child_state.get_substate(["grandchild_state"])
|
||||
|
||||
assert test_state.num1 == 0
|
||||
assert test_state.child_state.value == ""
|
||||
assert test_state.child_state2.value == ""
|
||||
assert test_state.child_state.count == 23
|
||||
assert test_state.child_state.grandchild_state.value2 == ""
|
||||
assert child_state.value == ""
|
||||
assert child_state2.value == ""
|
||||
assert child_state.count == 23
|
||||
assert grandchild_state.value2 == ""
|
||||
with pytest.raises(AttributeError):
|
||||
test_state.invalid
|
||||
with pytest.raises(AttributeError):
|
||||
@ -435,77 +438,15 @@ def test_set_child_attribute(TestState, ChildState, ChildState2, GrandchildState
|
||||
GrandchildState: The grandchild state class.
|
||||
"""
|
||||
test_state = TestState()
|
||||
child_state = test_state.child_state
|
||||
grandchild_state = child_state.grandchild_state
|
||||
child_state = test_state.get_substate(["child_state"])
|
||||
grandchild_state = child_state.get_substate(["grandchild_state"])
|
||||
|
||||
test_state.num1 = 10
|
||||
assert test_state.num1 == 10
|
||||
test_state.child_state.value = "test"
|
||||
assert test_state.child_state.value == "test"
|
||||
assert child_state.value == "test"
|
||||
|
||||
test_state.child_state.grandchild_state.value2 = "test2"
|
||||
assert test_state.child_state.grandchild_state.value2 == "test2"
|
||||
assert child_state.grandchild_state.value2 == "test2"
|
||||
assert grandchild_state.value2 == "test2"
|
||||
|
||||
|
||||
def test_get_parent_attribute(TestState, ChildState, ChildState2, GrandchildState):
|
||||
"""Test setting the attribute of a state.
|
||||
|
||||
Args:
|
||||
TestState: The state class.
|
||||
ChildState: The child state class.
|
||||
ChildState2: The child state class.
|
||||
GrandchildState: The grandchild state class.
|
||||
"""
|
||||
test_state = TestState()
|
||||
child_state = test_state.child_state
|
||||
grandchild_state = child_state.grandchild_state
|
||||
|
||||
assert test_state.num1 == 0
|
||||
assert child_state.num1 == 0
|
||||
assert grandchild_state.num1 == 0
|
||||
|
||||
# Changing the parent var should change the child var.
|
||||
test_state.num1 = 1
|
||||
assert test_state.num1 == 1
|
||||
assert child_state.num1 == 1
|
||||
assert grandchild_state.num1 == 1
|
||||
|
||||
child_state.value = "test"
|
||||
assert test_state.child_state.value == "test"
|
||||
assert child_state.value == "test"
|
||||
assert grandchild_state.value == "test"
|
||||
|
||||
|
||||
def test_set_parent_attribute(TestState, ChildState, ChildState2, GrandchildState):
|
||||
"""Test setting the attribute of a state.
|
||||
|
||||
Args:
|
||||
TestState: The state class.
|
||||
ChildState: The child state class.
|
||||
ChildState2: The child state class.
|
||||
GrandchildState: The grandchild state class.
|
||||
"""
|
||||
test_state = TestState()
|
||||
child_state = test_state.child_state
|
||||
grandchild_state = child_state.grandchild_state
|
||||
|
||||
# Changing the child var should not change the parent var.
|
||||
child_state.num1 = 2
|
||||
assert child_state.num1 == 2
|
||||
assert test_state.num1 == 2
|
||||
assert grandchild_state.num1 == 2
|
||||
|
||||
grandchild_state.num1 = 3
|
||||
assert grandchild_state.num1 == 3
|
||||
assert child_state.num1 == 3
|
||||
assert test_state.num1 == 3
|
||||
|
||||
grandchild_state.value = "test2"
|
||||
assert grandchild_state.value == "test2"
|
||||
assert child_state.value == "test2"
|
||||
grandchild_state.value2 = "test2"
|
||||
assert grandchild_state.value2 == "test2"
|
||||
|
||||
|
||||
def test_get_substate(TestState, ChildState, ChildState2, GrandchildState):
|
||||
@ -518,11 +459,12 @@ def test_get_substate(TestState, ChildState, ChildState2, GrandchildState):
|
||||
GrandchildState: The grandchild state class.
|
||||
"""
|
||||
test_state = TestState()
|
||||
child_state = test_state.child_state
|
||||
grandchild_state = child_state.grandchild_state
|
||||
child_state = test_state.substates["child_state"]
|
||||
child_state2 = test_state.substates["child_state2"]
|
||||
grandchild_state = child_state.substates["grandchild_state"]
|
||||
|
||||
assert test_state.get_substate(("child_state",)) == child_state
|
||||
assert test_state.get_substate(("child_state2",)) == test_state.child_state2
|
||||
assert test_state.get_substate(("child_state2",)) == child_state2
|
||||
assert (
|
||||
test_state.get_substate(("child_state", "grandchild_state")) == grandchild_state
|
||||
)
|
||||
@ -569,9 +511,9 @@ def test_set_dirty_substate(TestState, ChildState, ChildState2, GrandchildState)
|
||||
GrandchildState: The grandchild state class.
|
||||
"""
|
||||
test_state = TestState()
|
||||
child_state = test_state.child_state
|
||||
child_state2 = test_state.child_state2
|
||||
grandchild_state = child_state.grandchild_state
|
||||
child_state = test_state.get_substate(["child_state"])
|
||||
child_state2 = test_state.get_substate(["child_state2"])
|
||||
grandchild_state = child_state.get_substate(["grandchild_state"])
|
||||
|
||||
# Initially there should be no dirty vars.
|
||||
assert test_state.dirty_vars == set()
|
||||
@ -610,7 +552,7 @@ def test_reset(TestState, ChildState):
|
||||
ChildState: The child state class.
|
||||
"""
|
||||
test_state = TestState()
|
||||
child_state = test_state.child_state
|
||||
child_state = test_state.get_substate(["child_state"])
|
||||
|
||||
# Set some values.
|
||||
test_state.num1 = 1
|
||||
@ -664,8 +606,8 @@ async def test_process_event_substate(TestState, ChildState, GrandchildState):
|
||||
GrandchildState: The grandchild state class.
|
||||
"""
|
||||
test_state = TestState()
|
||||
child_state = test_state.child_state
|
||||
grandchild_state = child_state.grandchild_state
|
||||
child_state = test_state.get_substate(["child_state"])
|
||||
grandchild_state = child_state.get_substate(["grandchild_state"])
|
||||
|
||||
# Events should bubble down to the substate.
|
||||
assert child_state.value == ""
|
||||
@ -695,18 +637,3 @@ async def test_process_event_substate(TestState, ChildState, GrandchildState):
|
||||
"test_state.child_state.grandchild_state": {"value2": "new"},
|
||||
"test_state": {"sum": 3.14, "upper": ""},
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_process_event_substate_set_parent_state(TestState, ChildState):
|
||||
"""Test setting the parent state on a substate.
|
||||
|
||||
Args:
|
||||
TestState: The state class.
|
||||
ChildState: The child state class.
|
||||
"""
|
||||
test_state = TestState()
|
||||
event = Event(token="t", name="child_state.set_num1", payload={"value": 69})
|
||||
update = await test_state.process(event)
|
||||
assert test_state.num1 == 69
|
||||
assert update.delta == {"test_state": {"num1": 69, "sum": 72.14, "upper": ""}}
|
||||
|
Loading…
Reference in New Issue
Block a user