Make argument optional for event handlers (#950)

This commit is contained in:
Thomas Brandého 2023-05-06 22:10:35 +02:00 committed by GitHub
parent b4e534cc97
commit 893c0b132e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 30 additions and 25 deletions

View File

@ -220,7 +220,9 @@ class Component(Base, ABC):
event = call_event_handler(v, arg)
# 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(
f"Event handler: {v.fn} for uncontrolled event {event_trigger} should not take any args."
)

View File

@ -90,7 +90,7 @@ class EventSpec(Base):
local_args: Tuple[Var, ...] = ()
# The arguments to pass to the function.
args: Tuple[Tuple[Var, Var], ...] = ()
args: Optional[Tuple[Tuple[Var, Var], ...]] = ()
# Whether to upload files.
upload: bool = False
@ -318,7 +318,9 @@ def call_event_fn(fn: Callable, arg: Var) -> List[EventSpec]:
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.
Args:
@ -327,16 +329,14 @@ def get_handler_args(event_spec: EventSpec, arg: Var) -> Tuple[Tuple[Var, Var],
Returns:
The handler args.
Raises:
ValueError: If the event handler has an invalid signature.
"""
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),) if len(args) == 2 else None)
)
return event_spec.args if len(args) > 2 else ((Var.create_safe(args[1]), arg),)
def fix_events(
@ -369,7 +369,7 @@ def fix_events(
e = e()
assert isinstance(e, EventSpec), f"Unexpected event type, {type(e)}."
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.
out.append(

View File

@ -286,12 +286,18 @@ def format_event(event_spec: EventSpec) -> str:
Returns:
The compiled event.
"""
args = ",".join(
args = (
",".join(
[
":".join((name.name, json.dumps(val.name) if val.is_string else val.name))
":".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, '{')})"
@ -323,7 +329,7 @@ def format_full_control_event(event_chain: EventChain) -> str:
from pynecone.compiler import templates
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
chain = ",".join([format_event(event) for event in event_chain.events])
event = templates.FULL_CONTROL(state_name=state_name, arg=arg, chain=chain)

View File

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