Explorar el Código

Call special events from event triggers (#601)

Nikhil Rao hace 2 años
padre
commit
6c8ce67b8b
Se han modificado 5 ficheros con 71 adiciones y 32 borrados
  1. 7 2
      pynecone/components/component.py
  2. 27 15
      pynecone/event.py
  3. 3 2
      pynecone/state.py
  4. 8 12
      pynecone/utils.py
  5. 26 1
      tests/test_event.py

+ 7 - 2
pynecone/components/component.py

@@ -159,7 +159,9 @@ class Component(Base, ABC):
     def _create_event_chain(
     def _create_event_chain(
         self,
         self,
         event_trigger: str,
         event_trigger: str,
-        value: Union[Var, EventHandler, List[EventHandler], Callable],
+        value: Union[
+            Var, EventHandler, EventSpec, List[Union[EventHandler, EventSpec]], Callable
+        ],
     ) -> Union[EventChain, Var]:
     ) -> Union[EventChain, Var]:
         """Create an event chain from a variety of input types.
         """Create an event chain from a variety of input types.
 
 
@@ -186,7 +188,7 @@ class Component(Base, ABC):
         arg = controlled_triggers.get(event_trigger, EVENT_ARG)
         arg = controlled_triggers.get(event_trigger, EVENT_ARG)
 
 
         # If the input is a single event handler, wrap it in a list.
         # If the input is a single event handler, wrap it in a list.
-        if isinstance(value, EventHandler):
+        if isinstance(value, (EventHandler, EventSpec)):
             value = [value]
             value = [value]
 
 
         # If the input is a list of event handlers, create an event chain.
         # If the input is a list of event handlers, create an event chain.
@@ -205,6 +207,9 @@ class Component(Base, ABC):
 
 
                     # Add the event to the chain.
                     # Add the event to the chain.
                     events.append(event)
                     events.append(event)
+                elif isinstance(v, EventSpec):
+                    # Add the event to the chain.
+                    events.append(v)
                 elif isinstance(v, Callable):
                 elif isinstance(v, Callable):
                     # Call the lambda to get the event chain.
                     # Call the lambda to get the event chain.
                     events.extend(utils.call_event_fn(v, arg))
                     events.extend(utils.call_event_fn(v, arg))

+ 27 - 15
pynecone/event.py

@@ -123,7 +123,7 @@ EVENT_ARG = BaseVar(name="_e", type_=FrontendEvent, is_local=True)
 
 
 
 
 # Special server-side events.
 # Special server-side events.
-def redirect(path: str) -> Event:
+def redirect(path: str) -> EventSpec:
     """Redirect to a new path.
     """Redirect to a new path.
 
 
     Args:
     Args:
@@ -132,14 +132,18 @@ def redirect(path: str) -> Event:
     Returns:
     Returns:
         An event to redirect to the path.
         An event to redirect to the path.
     """
     """
-    return Event(
-        token="",
-        name="_redirect",
-        payload={"path": path},
+
+    def fn():
+        return None
+
+    fn.__qualname__ = "_redirect"
+    return EventSpec(
+        handler=EventHandler(fn=fn),
+        args=(("path", path),),
     )
     )
 
 
 
 
-def console_log(message: str) -> Event:
+def console_log(message: str) -> EventSpec:
     """Do a console.log on the browser.
     """Do a console.log on the browser.
 
 
     Args:
     Args:
@@ -148,14 +152,18 @@ def console_log(message: str) -> Event:
     Returns:
     Returns:
         An event to log the message.
         An event to log the message.
     """
     """
-    return Event(
-        token="",
-        name="_console",
-        payload={"message": message},
+
+    def fn():
+        return None
+
+    fn.__qualname__ = "_console"
+    return EventSpec(
+        handler=EventHandler(fn=fn),
+        args=(("message", message),),
     )
     )
 
 
 
 
-def window_alert(message: str) -> Event:
+def window_alert(message: str) -> EventSpec:
     """Create a window alert on the browser.
     """Create a window alert on the browser.
 
 
     Args:
     Args:
@@ -164,10 +172,14 @@ def window_alert(message: str) -> Event:
     Returns:
     Returns:
         An event to alert the message.
         An event to alert the message.
     """
     """
-    return Event(
-        token="",
-        name="_alert",
-        payload={"message": message},
+
+    def fn():
+        return None
+
+    fn.__qualname__ = "_alert"
+    return EventSpec(
+        handler=EventHandler(fn=fn),
+        args=(("message", message),),
     )
     )
 
 
 
 

+ 3 - 2
pynecone/state.py

@@ -545,9 +545,10 @@ class State(Base, ABC):
         except Exception:
         except Exception:
             error = traceback.format_exc()
             error = traceback.format_exc()
             print(error)
             print(error)
-            return StateUpdate(
-                events=[window_alert("An error occurred. See logs for details.")]
+            events = utils.fix_events(
+                [window_alert("An error occurred. See logs for details.")], event.token
             )
             )
+            return StateUpdate(events=events)
 
 
         # Fix the returned events.
         # Fix the returned events.
         events = utils.fix_events(events, event.token)
         events = utils.fix_events(events, event.token)

+ 8 - 12
pynecone/utils.py

@@ -1421,7 +1421,9 @@ def get_handler_args(event_spec: EventSpec, arg: Var) -> Tuple[Tuple[str, str],
     return event_spec.args if len(args) > 2 else ((args[1], arg.name),)
     return event_spec.args if len(args) > 2 else ((args[1], arg.name),)
 
 
 
 
-def fix_events(events: Optional[List[Event]], token: str) -> List[Event]:
+def fix_events(
+    events: Optional[List[Union[EventHandler, EventSpec]]], token: str
+) -> List[Event]:
     """Fix a list of events returned by an event handler.
     """Fix a list of events returned by an event handler.
 
 
     Args:
     Args:
@@ -1444,18 +1446,12 @@ def fix_events(events: Optional[List[Event]], token: str) -> List[Event]:
     # Fix the events created by the handler.
     # Fix the events created by the handler.
     out = []
     out = []
     for e in events:
     for e in events:
-        # If it is already an event, don't modify it.
-        if isinstance(e, Event):
-            name = e.name
-            payload = e.payload
-
         # Otherwise, create an event from the event spec.
         # Otherwise, create an event from the event spec.
-        else:
-            if isinstance(e, EventHandler):
-                e = e()
-            assert isinstance(e, EventSpec), f"Unexpected event type, {type(e)}."
-            name = format_event_handler(e.handler)
-            payload = dict(e.args)
+        if isinstance(e, EventHandler):
+            e = e()
+        assert isinstance(e, EventSpec), f"Unexpected event type, {type(e)}."
+        name = format_event_handler(e.handler)
+        payload = dict(e.args)
 
 
         # Create an event and append it to the list.
         # Create an event and append it to the list.
         out.append(
         out.append(

+ 26 - 1
tests/test_event.py

@@ -1,4 +1,5 @@
-from pynecone.event import Event, EventHandler
+from pynecone import event
+from pynecone.event import Event, EventHandler, EventSpec
 from pynecone.var import Var
 from pynecone.var import Var
 
 
 
 
@@ -46,3 +47,27 @@ def test_call_event_handler():
     assert event_spec.handler == handler
     assert event_spec.handler == handler
     assert event_spec.local_args == ()
     assert event_spec.local_args == ()
     assert event_spec.args == (("arg1", "first"), ("arg2", "second"))
     assert event_spec.args == (("arg1", "first"), ("arg2", "second"))
+
+
+def test_event_redirect():
+    """Test the event redirect function."""
+    spec = event.redirect("/path")
+    assert isinstance(spec, EventSpec)
+    assert spec.handler.fn.__qualname__ == "_redirect"
+    assert spec.args == (("path", "/path"),)
+
+
+def test_event_console_log():
+    """Test the event console log function."""
+    spec = event.console_log("message")
+    assert isinstance(spec, EventSpec)
+    assert spec.handler.fn.__qualname__ == "_console"
+    assert spec.args == (("message", "message"),)
+
+
+def test_event_window_alert():
+    """Test the event window alert function."""
+    spec = event.window_alert("message")
+    assert isinstance(spec, EventSpec)
+    assert spec.handler.fn.__qualname__ == "_alert"
+    assert spec.args == (("message", "message"),)