diff --git a/pynecone/components/component.py b/pynecone/components/component.py index b9f3b2e5c..a175a8003 100644 --- a/pynecone/components/component.py +++ b/pynecone/components/component.py @@ -159,7 +159,9 @@ class Component(Base, ABC): def _create_event_chain( self, event_trigger: str, - value: Union[Var, EventHandler, List[EventHandler], Callable], + value: Union[ + Var, EventHandler, EventSpec, List[Union[EventHandler, EventSpec]], Callable + ], ) -> Union[EventChain, Var]: """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) # If the input is a single event handler, wrap it in a list. - if isinstance(value, EventHandler): + if isinstance(value, (EventHandler, EventSpec)): value = [value] # 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. events.append(event) + elif isinstance(v, EventSpec): + # Add the event to the chain. + events.append(v) elif isinstance(v, Callable): # Call the lambda to get the event chain. events.extend(utils.call_event_fn(v, arg)) diff --git a/pynecone/event.py b/pynecone/event.py index 6d0b447b7..a09248b78 100644 --- a/pynecone/event.py +++ b/pynecone/event.py @@ -123,7 +123,7 @@ EVENT_ARG = BaseVar(name="_e", type_=FrontendEvent, is_local=True) # Special server-side events. -def redirect(path: str) -> Event: +def redirect(path: str) -> EventSpec: """Redirect to a new path. Args: @@ -132,14 +132,18 @@ def redirect(path: str) -> Event: Returns: 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. Args: @@ -148,14 +152,18 @@ def console_log(message: str) -> Event: Returns: 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. Args: @@ -164,10 +172,14 @@ def window_alert(message: str) -> Event: Returns: 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),), ) diff --git a/pynecone/state.py b/pynecone/state.py index 0417b7380..d0e4aa2f8 100644 --- a/pynecone/state.py +++ b/pynecone/state.py @@ -545,9 +545,10 @@ class State(Base, ABC): except Exception: error = traceback.format_exc() 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. events = utils.fix_events(events, event.token) diff --git a/pynecone/utils.py b/pynecone/utils.py index 330ed4a1f..5d6551646 100644 --- a/pynecone/utils.py +++ b/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),) -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. Args: @@ -1444,18 +1446,12 @@ def fix_events(events: Optional[List[Event]], token: str) -> List[Event]: # Fix the events created by the handler. out = [] 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. - 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. out.append( diff --git a/tests/test_event.py b/tests/test_event.py index c4f648ea3..a4ef690df 100644 --- a/tests/test_event.py +++ b/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 @@ -46,3 +47,27 @@ def test_call_event_handler(): assert event_spec.handler == handler assert event_spec.local_args == () 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"),)