فهرست منبع

Improve event processing performance (#163)

Nikhil Rao 2 سال پیش
والد
کامیت
974d2b4cbb
5فایلهای تغییر یافته به همراه44 افزوده شده و 162 حذف شده
  1. 2 2
      pynecone/constants.py
  2. 15 60
      pynecone/state.py
  3. 1 1
      pynecone/utils.py
  4. 5 5
      tests/test_app.py
  5. 21 94
      tests/test_state.py

+ 2 - 2
pynecone/constants.py

@@ -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.

+ 15 - 60
pynecone/state.py

@@ -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.

+ 1 - 1
pynecone/utils.py

@@ -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

+ 5 - 5
tests/test_app.py

@@ -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

+ 21 - 94
tests/test_state.py

@@ -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": ""}}