Skip to content

Commit 4649bf1

Browse files
committed
Handle args_spec partial args for FunctionVar
When a FunctionVar is passed to an event trigger with a given args_spec, transform it into a partial function that applies the transformed arguments when called. This allows FunctionVar handlers to work with on_blur and on_submit, which, by default, use the args_spec to transform the value before passing it off to the handler. Some escape hatches: * The behavior for EventChainVar is unchanged, so previous code that was explicitly casting functions to EventChain will continue to work without modification. * If the FunctionVar is returned through a lambda, no partial application is applied, because that happens at the point the lambda is called, so the return value of the lambda is responsible for mapping the arguments if desired. This change also allows event handler lambda functions to return a heterogeneous mix of EventSpec/EventHandler/FunctionVar (and EventChain returned from lambda are treated as FunctionVar, allowing arbitrary nesting). Update FunctionVar.partial such that passing no args does NOT create a new useless function.
1 parent 6fb6480 commit 4649bf1

2 files changed

Lines changed: 20 additions & 20 deletions

File tree

reflex/event.py

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -520,21 +520,14 @@ def create(
520520
if isinstance(v, (EventHandler, EventSpec)):
521521
# Call the event handler to get the event.
522522
events.append(call_event_handler(v, args_spec, key=key))
523-
elif isinstance(v, (EventVar, FunctionVar)):
523+
elif isinstance(v, (EventVar, EventChainVar)):
524524
events.append(v)
525+
elif isinstance(v, FunctionVar):
526+
# Apply the args_spec transformations as partial arguments to the function.
527+
events.append(v.partial(*parse_args_spec(args_spec)[0]))
525528
elif isinstance(v, Callable):
526529
# Call the lambda to get the event chain.
527-
result = call_event_fn(v, args_spec, key=key)
528-
if isinstance(result, Var):
529-
if isinstance(result, (EventVar, FunctionVar)):
530-
events.append(result)
531-
continue
532-
msg = (
533-
f"Invalid event chain: {v}. Cannot use a Var-returning "
534-
"lambda inside an EventChain list."
535-
)
536-
raise ValueError(msg)
537-
events.extend(result)
530+
events.extend(call_event_fn(v, args_spec, key=key))
538531
else:
539532
msg = f"Invalid event: {v}"
540533
raise ValueError(msg)
@@ -1783,7 +1776,7 @@ def call_event_fn(
17831776
fn: Callable,
17841777
arg_spec: ArgsSpec | Sequence[ArgsSpec],
17851778
key: str | None = None,
1786-
) -> list[EventSpec] | Var:
1779+
) -> list[EventSpec | FunctionVar]:
17871780
"""Call a function to a list of event specs.
17881781
17891782
The function should return a single EventSpec, a list of EventSpecs, or a
@@ -1816,10 +1809,6 @@ def call_event_fn(
18161809
# Call the function with the parsed args.
18171810
out = fn(*[*parsed_args][:number_of_fn_args])
18181811

1819-
# If the function returns a Var, assume it's an EventChain and render it directly.
1820-
if isinstance(out, Var):
1821-
return out
1822-
18231812
# Convert the output to a list.
18241813
if not isinstance(out, list):
18251814
out = [out]
@@ -1831,9 +1820,20 @@ def call_event_fn(
18311820
# An un-called EventHandler gets all of the args of the event trigger.
18321821
e = call_event_handler(e, arg_spec, key=key)
18331822

1823+
if isinstance(e, EventChain):
1824+
# Nested EventChain is treated like a FunctionVar.
1825+
e = Var.create(e)
1826+
18341827
# Make sure the event spec is valid.
1835-
if not isinstance(e, EventSpec):
1836-
msg = f"Lambda {fn} returned an invalid event spec: {e}."
1828+
if not isinstance(e, (EventSpec, FunctionVar)):
1829+
hint = ""
1830+
if isinstance(e, VarOperationCall):
1831+
hint = " Hint: use `fn.partial(...)` instead of calling the FunctionVar directly."
1832+
msg = (
1833+
f"Invalid event chain for {key}: {fn} -> {e}: A lambda inside an EventChain "
1834+
"list must return `EventSpec | EventHandler | FunctionVar` or heterogeneous list of these types. "
1835+
f"Got: {type(e)}.{hint}"
1836+
)
18371837
raise EventHandlerValueError(msg)
18381838

18391839
# Add the event spec to the chain.

reflex/vars/function.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ def partial(self, *args: Var | Any) -> FunctionVar: # pyright: ignore [reportIn
186186
The partially applied function.
187187
"""
188188
if not args:
189-
return ArgsFunctionOperation.create((), self)
189+
return self
190190
return ArgsFunctionOperation.create(
191191
("...args",),
192192
VarOperationCall.create(self, *args, Var(_js_expr="...args")),

0 commit comments

Comments
 (0)