-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Support FunctionVar handlers in EventChain rendering #6188
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 10 commits
6656b04
e75563d
7d3c191
03bbbf5
84f9b93
c2380bc
e11d0cd
76cbdf7
1d7e57e
df626d0
6fb6480
4649bf1
42282f7
c516ce8
cfa5a52
7a48fe3
6249a4b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,7 @@ | |
| import inspect | ||
| import sys | ||
| import types | ||
| import warnings | ||
| from base64 import b64encode | ||
| from collections.abc import Callable, Mapping, Sequence | ||
| from functools import lru_cache, partial | ||
|
|
@@ -449,8 +450,8 @@ def __call__(self, *args, **kwargs) -> EventSpec: | |
| class EventChain(EventActionsMixin): | ||
| """Container for a chain of events that will be executed in order.""" | ||
|
|
||
| events: Sequence["EventSpec | EventVar | EventCallback"] = dataclasses.field( | ||
| default_factory=list | ||
| events: Sequence["EventSpec | EventVar | FunctionVar | EventCallback"] = ( | ||
| dataclasses.field(default_factory=list) | ||
| ) | ||
|
|
||
| args_spec: Callable | Sequence[Callable] | None = dataclasses.field(default=None) | ||
|
|
@@ -481,9 +482,18 @@ def create( | |
| """ | ||
| # If it's an event chain var, return it. | ||
| if isinstance(value, Var): | ||
| if isinstance(value, EventChainVar): | ||
| # Only pass through literal/prebuilt chains. Other EventChainVar values may be | ||
| # FunctionVars cast with `.to(EventChain)` and still need wrapping so | ||
| # event_chain_kwargs can compose onto the resulting chain. | ||
| if isinstance(value, LiteralEventChainVar): | ||
| if event_chain_kwargs: | ||
| warnings.warn( | ||
| f"event_chain_kwargs {event_chain_kwargs!r} are ignored for " | ||
| "EventChainVar values.", | ||
| stacklevel=2, | ||
| ) | ||
| return value | ||
|
masenf marked this conversation as resolved.
|
||
| if isinstance(value, EventVar): | ||
| if isinstance(value, (EventVar, FunctionVar)): | ||
| value = [value] | ||
| elif safe_issubclass(value._var_type, (EventChain, EventSpec)): | ||
| return cls.create( | ||
|
|
@@ -505,23 +515,26 @@ def create( | |
|
|
||
| # If the input is a list of event handlers, create an event chain. | ||
| if isinstance(value, list): | ||
| events: list[EventSpec | EventVar] = [] | ||
| events: list[EventSpec | EventVar | FunctionVar] = [] | ||
| for v in value: | ||
| if isinstance(v, (EventHandler, EventSpec)): | ||
| # Call the event handler to get the event. | ||
| events.append(call_event_handler(v, args_spec, key=key)) | ||
| elif isinstance(v, (EventVar, FunctionVar)): | ||
| events.append(v) | ||
| elif isinstance(v, Callable): | ||
| # Call the lambda to get the event chain. | ||
| result = call_event_fn(v, args_spec, key=key) | ||
| if isinstance(result, Var): | ||
| if isinstance(result, (EventVar, FunctionVar)): | ||
| events.append(result) | ||
| continue | ||
| msg = ( | ||
| f"Invalid event chain: {v}. Cannot use a Var-returning " | ||
| "lambda inside an EventChain list." | ||
| ) | ||
| raise ValueError(msg) | ||
| events.extend(result) | ||
| elif isinstance(v, EventVar): | ||
| events.append(v) | ||
| else: | ||
| msg = f"Invalid event: {v}" | ||
| raise ValueError(msg) | ||
|
|
@@ -2075,9 +2088,11 @@ def create( | |
| else value.args_spec | ||
| ) | ||
| sig = inspect.signature(arg_spec) # pyright: ignore [reportArgumentType] | ||
| arg_vars = () | ||
| if sig.parameters: | ||
| arg_def = tuple(f"_{p}" for p in sig.parameters) | ||
| arg_def_expr = LiteralVar.create([Var(_js_expr=arg) for arg in arg_def]) | ||
| arg_vars = tuple(Var(_js_expr=arg) for arg in arg_def) | ||
| arg_def_expr = LiteralVar.create(list(arg_vars)) | ||
| else: | ||
| # add a default argument for addEvents if none were specified in value.args_spec | ||
| # used to trigger the preventDefault() on the event. | ||
|
|
@@ -2098,17 +2113,55 @@ def create( | |
| if invocation is not None and not isinstance(invocation, FunctionVar): | ||
| msg = f"EventChain invocation must be a FunctionVar, got {invocation!s} of type {invocation._var_type!s}." | ||
| raise ValueError(msg) | ||
| assert invocation is not None | ||
|
|
||
| call_args = arg_vars if sig.parameters else (Var(_js_expr="...args"),) | ||
| statements = [ | ||
| ( | ||
| event.call(*call_args) | ||
| if isinstance(event, FunctionVar) | ||
| else invocation.call( | ||
| LiteralVar.create([LiteralVar.create(event)]), | ||
| arg_def_expr, | ||
| {}, | ||
| ) | ||
| ) | ||
| for event in value.events | ||
| ] | ||
|
|
||
| if not statements: | ||
| statements.append(invocation.call(LiteralVar.create([]), arg_def_expr, {})) | ||
|
|
||
| statement_var_data = [statement._get_all_var_data() for statement in statements] | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i think this might be the line that is causing the CodSpeed regression. usually if you just combine the vars, the parser will pick up any nested VarData automatically: statement_block = Var(
_js_expr=f"{{{''.join(f'{statement};' for statement in statements)}}}",
)i would expect the above to be equivalent to how it was written without the explicit VarData merge
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You were right, it is fixed now. |
||
| if len(statements) == 1 and not value.event_actions: | ||
| return_expr = statements[0] | ||
| else: | ||
| statement_block = Var( | ||
| _js_expr=f"{{{''.join(f'{statement!s};' for statement in statements)}}}", | ||
| _var_data=VarData.merge(*statement_var_data), | ||
| ) | ||
| if value.event_actions: | ||
| apply_event_actions = FunctionStringVar.create( | ||
| CompileVars.APPLY_EVENT_ACTIONS, | ||
| _var_data=VarData( | ||
| imports=Imports.EVENTS, | ||
| hooks={Hooks.EVENTS: None}, | ||
| ), | ||
| ) | ||
| return_expr = apply_event_actions.call( | ||
| ArgsFunctionOperation.create((), statement_block), | ||
| value.event_actions, | ||
| *call_args, | ||
| ) | ||
| else: | ||
| return_expr = statement_block | ||
|
|
||
| return cls( | ||
| _js_expr="", | ||
| _var_type=EventChain, | ||
| _var_data=_var_data, | ||
| _args=FunctionArgs(arg_def), | ||
| _return_expr=invocation.call( | ||
| LiteralVar.create([LiteralVar.create(event) for event in value.events]), | ||
| arg_def_expr, | ||
| value.event_actions, | ||
| ), | ||
| _return_expr=return_expr, | ||
| _var_value=value, | ||
| ) | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.