Bladeren bron

Make argument optional for event handlers (#950)

Thomas Brandého 2 jaren geleden
bovenliggende
commit
893c0b132e
4 gewijzigde bestanden met toevoegingen van 30 en 25 verwijderingen
  1. 3 1
      pynecone/components/component.py
  2. 11 11
      pynecone/event.py
  3. 12 6
      pynecone/utils/format.py
  4. 4 7
      tests/components/test_component.py

+ 3 - 1
pynecone/components/component.py

@@ -220,7 +220,9 @@ class Component(Base, ABC):
                     event = call_event_handler(v, arg)
                     event = call_event_handler(v, arg)
 
 
                     # Check that the event handler takes no args if it's uncontrolled.
                     # Check that the event handler takes no args if it's uncontrolled.
-                    if not is_controlled_event and len(event.args) > 0:
+                    if not is_controlled_event and (
+                        event.args is not None and len(event.args) > 0
+                    ):
                         raise ValueError(
                         raise ValueError(
                             f"Event handler: {v.fn} for uncontrolled event {event_trigger} should not take any args."
                             f"Event handler: {v.fn} for uncontrolled event {event_trigger} should not take any args."
                         )
                         )

+ 11 - 11
pynecone/event.py

@@ -90,7 +90,7 @@ class EventSpec(Base):
     local_args: Tuple[Var, ...] = ()
     local_args: Tuple[Var, ...] = ()
 
 
     # The arguments to pass to the function.
     # The arguments to pass to the function.
-    args: Tuple[Tuple[Var, Var], ...] = ()
+    args: Optional[Tuple[Tuple[Var, Var], ...]] = ()
 
 
     # Whether to upload files.
     # Whether to upload files.
     upload: bool = False
     upload: bool = False
@@ -318,7 +318,9 @@ def call_event_fn(fn: Callable, arg: Var) -> List[EventSpec]:
     return events
     return events
 
 
 
 
-def get_handler_args(event_spec: EventSpec, arg: Var) -> Tuple[Tuple[Var, Var], ...]:
+def get_handler_args(
+    event_spec: EventSpec, arg: Var
+) -> Optional[Tuple[Tuple[Var, Var], ...]]:
     """Get the handler args for the given event spec.
     """Get the handler args for the given event spec.
 
 
     Args:
     Args:
@@ -327,16 +329,14 @@ def get_handler_args(event_spec: EventSpec, arg: Var) -> Tuple[Tuple[Var, Var],
 
 
     Returns:
     Returns:
         The handler args.
         The handler args.
-
-    Raises:
-        ValueError: If the event handler has an invalid signature.
     """
     """
     args = inspect.getfullargspec(event_spec.handler.fn).args
     args = inspect.getfullargspec(event_spec.handler.fn).args
-    if len(args) < 2:
-        raise ValueError(
-            f"Event handler has an invalid signature, needed a method with a parameter, got {event_spec.handler}."
-        )
-    return event_spec.args if len(args) > 2 else ((Var.create_safe(args[1]), arg),)
+
+    return (
+        event_spec.args
+        if len(args) > 2
+        else (((Var.create_safe(args[1]), arg),) if len(args) == 2 else None)
+    )
 
 
 
 
 def fix_events(
 def fix_events(
@@ -369,7 +369,7 @@ def fix_events(
             e = e()
             e = e()
         assert isinstance(e, EventSpec), f"Unexpected event type, {type(e)}."
         assert isinstance(e, EventSpec), f"Unexpected event type, {type(e)}."
         name = format.format_event_handler(e.handler)
         name = format.format_event_handler(e.handler)
-        payload = {k.name: v.name for k, v in e.args}
+        payload = {k.name: v.name for k, v in e.args} if e.args else {}
 
 
         # Create an event and append it to the list.
         # Create an event and append it to the list.
         out.append(
         out.append(

+ 12 - 6
pynecone/utils/format.py

@@ -286,11 +286,17 @@ def format_event(event_spec: EventSpec) -> str:
     Returns:
     Returns:
         The compiled event.
         The compiled event.
     """
     """
-    args = ",".join(
-        [
-            ":".join((name.name, json.dumps(val.name) if val.is_string else val.name))
-            for name, val in event_spec.args
-        ]
+    args = (
+        ",".join(
+            [
+                ":".join(
+                    (name.name, json.dumps(val.name) if val.is_string else val.name)
+                )
+                for name, val in event_spec.args
+            ]
+        )
+        if event_spec.args is not None
+        else ""
     )
     )
     return f"E(\"{format_event_handler(event_spec.handler)}\", {wrap(args, '{')})"
     return f"E(\"{format_event_handler(event_spec.handler)}\", {wrap(args, '{')})"
 
 
@@ -323,7 +329,7 @@ def format_full_control_event(event_chain: EventChain) -> str:
     from pynecone.compiler import templates
     from pynecone.compiler import templates
 
 
     event_spec = event_chain.events[0]
     event_spec = event_chain.events[0]
-    arg = event_spec.args[0][1]
+    arg = event_spec.args[0][1] if event_spec.args else None
     state_name = event_chain.state_name
     state_name = event_chain.state_name
     chain = ",".join([format_event(event) for event in event_chain.events])
     chain = ",".join([format_event(event) for event in event_chain.events])
     event = templates.FULL_CONTROL(state_name=state_name, arg=arg, chain=chain)
     event = templates.FULL_CONTROL(state_name=state_name, arg=arg, chain=chain)

+ 4 - 7
tests/components/test_component.py

@@ -391,13 +391,10 @@ def test_invalid_event_handler_args(component2, test_state):
     # Controlled event handlers should take args.
     # Controlled event handlers should take args.
     # This is okay.
     # This is okay.
     component2.create(on_open=test_state.do_something_arg)
     component2.create(on_open=test_state.do_something_arg)
-    # This is not okay.
-    with pytest.raises(ValueError):
-        component2.create(on_open=test_state.do_something)
-    with pytest.raises(ValueError):
-        component2.create(
-            on_open=[test_state.do_something_arg, test_state.do_something]
-        )
+
+    # do_something is allowed and will simply run while ignoring the arg
+    component2.create(on_open=test_state.do_something)
+    component2.create(on_open=[test_state.do_something_arg, test_state.do_something])
 
 
 
 
 def test_get_hooks_nested(component1, component2, component3):
 def test_get_hooks_nested(component1, component2, component3):