diff --git a/reflex/components/component.py b/reflex/components/component.py index 364353b9d..d456ae762 100644 --- a/reflex/components/component.py +++ b/reflex/components/component.py @@ -469,6 +469,7 @@ class Component(BaseComponent, ABC): kwargs["event_triggers"][key] = self._create_event_chain( value=value, # type: ignore args_spec=component_specific_triggers[key], + key=key, ) # Remove any keys that were added as events. @@ -529,12 +530,14 @@ class Component(BaseComponent, ABC): List[Union[EventHandler, EventSpec, EventVar]], Callable, ], + key: Optional[str] = None, ) -> Union[EventChain, Var]: """Create an event chain from a variety of input types. Args: args_spec: The args_spec of the event trigger being bound. value: The value to create the event chain from. + key: The key of the event trigger being bound. Returns: The event chain. @@ -549,7 +552,7 @@ class Component(BaseComponent, ABC): elif isinstance(value, EventVar): value = [value] elif issubclass(value._var_type, (EventChain, EventSpec)): - return self._create_event_chain(args_spec, value.guess_type()) + return self._create_event_chain(args_spec, value.guess_type(), key=key) else: raise ValueError( f"Invalid event chain: {str(value)} of type {value._var_type}" @@ -568,10 +571,10 @@ class Component(BaseComponent, ABC): 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)) + events.append(call_event_handler(v, args_spec, key=key)) elif isinstance(v, Callable): # Call the lambda to get the event chain. - result = call_event_fn(v, args_spec) + result = call_event_fn(v, args_spec, key=key) if isinstance(result, Var): raise ValueError( f"Invalid event chain: {v}. Cannot use a Var-returning " @@ -588,7 +591,7 @@ class Component(BaseComponent, ABC): result = call_event_fn(value, args_spec) if isinstance(result, Var): # Recursively call this function if the lambda returned an EventChain Var. - return self._create_event_chain(args_spec, result) + return self._create_event_chain(args_spec, result, key=key) events = [*result] # Otherwise, raise an error. @@ -1707,6 +1710,7 @@ class CustomComponent(Component): args_spec=event_triggers_in_component_declaration.get( key, lambda: [] ), + key=key, ) self.props[format.to_camel_case(key)] = value continue diff --git a/reflex/event.py b/reflex/event.py index a0c62a6b0..e211fd351 100644 --- a/reflex/event.py +++ b/reflex/event.py @@ -976,6 +976,7 @@ def get_hydrate_event(state) -> str: def call_event_handler( event_handler: EventHandler | EventSpec, arg_spec: ArgsSpec, + key: Optional[str] = None, ) -> EventSpec: """Call an event handler to get the event spec. @@ -986,6 +987,7 @@ def call_event_handler( Args: event_handler: The event handler. arg_spec: The lambda that define the argument(s) to pass to the event handler. + key: The key to pass to the event handler. Raises: EventHandlerArgMismatch: if number of arguments expected by event_handler doesn't match the spec. @@ -1061,14 +1063,14 @@ def call_event_handler( ) except TypeError as e: raise TypeError( - f"Could not compare types {args_types_without_vars[i]} and {type_hints_of_provided_callback[arg]} for argument {arg} of {event_handler.fn.__qualname__}." + f"Could not compare types {args_types_without_vars[i]} and {type_hints_of_provided_callback[arg]} for argument {arg} of {event_handler.fn.__qualname__} provided for {key}." ) from e if compare_result: continue else: raise EventHandlerArgTypeMismatch( - f"Event handler {event_handler.fn.__qualname__} expects {args_types_without_vars[i]} for argument {arg} but got {type_hints_of_provided_callback[arg]} instead." + f"Event handler {key} expects {args_types_without_vars[i]} for argument {arg} but got {type_hints_of_provided_callback[arg]} from {event_handler.fn.__qualname__} instead." ) return event_handler(*parsed_args) # type: ignore @@ -1134,13 +1136,18 @@ def parse_args_spec(arg_spec: ArgsSpec): ) -def check_fn_match_arg_spec(fn: Callable, arg_spec: ArgsSpec) -> List[Var]: +def check_fn_match_arg_spec( + fn: Callable, + arg_spec: ArgsSpec, + key: Optional[str] = None, +) -> List[Var]: """Ensures that the function signature matches the passed argument specification or raises an EventFnArgMismatch if they do not. Args: fn: The function to be validated. arg_spec: The argument specification for the event trigger. + key: The key to pass to the event handler. Returns: The parsed arguments from the argument specification. @@ -1166,7 +1173,11 @@ def check_fn_match_arg_spec(fn: Callable, arg_spec: ArgsSpec) -> List[Var]: return parsed_args -def call_event_fn(fn: Callable, arg_spec: ArgsSpec) -> list[EventSpec] | Var: +def call_event_fn( + fn: Callable, + arg_spec: ArgsSpec, + key: Optional[str] = None, +) -> list[EventSpec] | Var: """Call a function to a list of event specs. The function should return a single EventSpec, a list of EventSpecs, or a @@ -1175,6 +1186,7 @@ def call_event_fn(fn: Callable, arg_spec: ArgsSpec) -> list[EventSpec] | Var: Args: fn: The function to call. arg_spec: The argument spec for the event trigger. + key: The key to pass to the event handler. Returns: The event specs from calling the function or a Var. @@ -1187,7 +1199,7 @@ def call_event_fn(fn: Callable, arg_spec: ArgsSpec) -> list[EventSpec] | Var: from reflex.utils.exceptions import EventHandlerValueError # Check that fn signature matches arg_spec - parsed_args = check_fn_match_arg_spec(fn, arg_spec) + parsed_args = check_fn_match_arg_spec(fn, arg_spec, key=key) # Call the function with the parsed args. out = fn(*parsed_args) @@ -1205,7 +1217,7 @@ def call_event_fn(fn: Callable, arg_spec: ArgsSpec) -> list[EventSpec] | Var: for e in out: if isinstance(e, EventHandler): # An un-called EventHandler gets all of the args of the event trigger. - e = call_event_handler(e, arg_spec) + e = call_event_handler(e, arg_spec, key=key) # Make sure the event spec is valid. if not isinstance(e, EventSpec):