1
0
Эх сурвалжийг харах

Allow calling event handlers with keyword arguments (#5124)

* Allow calling event handlers with keyword arguments

* why is upload so weird
Khaleel Al-Adhami 1 сар өмнө
parent
commit
ce661891af

+ 1 - 1
pyi_hashes.json

@@ -20,7 +20,7 @@
   "reflex/components/core/debounce.pyi": "76d857eb814bc64625860a5f43e8b230",
   "reflex/components/core/debounce.pyi": "76d857eb814bc64625860a5f43e8b230",
   "reflex/components/core/html.pyi": "b12117b42ef79ee90b6b4dec50baeb86",
   "reflex/components/core/html.pyi": "b12117b42ef79ee90b6b4dec50baeb86",
   "reflex/components/core/sticky.pyi": "c65131cf7c2312c68e1fddaa0cc27150",
   "reflex/components/core/sticky.pyi": "c65131cf7c2312c68e1fddaa0cc27150",
-  "reflex/components/core/upload.pyi": "6fb89607ec8f8c1971f0dbd3453901eb",
+  "reflex/components/core/upload.pyi": "16bf18a95d830184a1ae6c177e91e529",
   "reflex/components/datadisplay/__init__.pyi": "cf087efa8b3960decc6b231cc986cfa9",
   "reflex/components/datadisplay/__init__.pyi": "cf087efa8b3960decc6b231cc986cfa9",
   "reflex/components/datadisplay/code.pyi": "3d8f0ab4c2f123d7f80d15c7ebc553d9",
   "reflex/components/datadisplay/code.pyi": "3d8f0ab4c2f123d7f80d15c7ebc553d9",
   "reflex/components/datadisplay/dataeditor.pyi": "1b762071001161e4fdd1285263c33bb3",
   "reflex/components/datadisplay/dataeditor.pyi": "1b762071001161e4fdd1285263c33bb3",

+ 5 - 9
reflex/components/core/upload.py

@@ -23,6 +23,7 @@ from reflex.event import (
     EventHandler,
     EventHandler,
     EventSpec,
     EventSpec,
     call_event_fn,
     call_event_fn,
+    call_event_handler,
     parse_args_spec,
     parse_args_spec,
     run_script,
     run_script,
 )
 )
@@ -265,17 +266,12 @@ class Upload(MemoizationLeaf):
             upload_props["on_drop"] = upload_file(upload_props["id"])
             upload_props["on_drop"] = upload_file(upload_props["id"])
         else:
         else:
             on_drop = upload_props["on_drop"]
             on_drop = upload_props["on_drop"]
-            if isinstance(on_drop, Callable):
+            if isinstance(on_drop, (EventHandler, EventSpec)):
+                # Call the lambda to get the event chain.
+                on_drop = call_event_handler(on_drop, _on_drop_spec)
+            elif isinstance(on_drop, Callable):
                 # Call the lambda to get the event chain.
                 # Call the lambda to get the event chain.
                 on_drop = call_event_fn(on_drop, _on_drop_spec)
                 on_drop = call_event_fn(on_drop, _on_drop_spec)
-            if isinstance(on_drop, EventSpec):
-                # Update the provided args for direct use with on_drop.
-                on_drop = on_drop.with_args(
-                    args=tuple(
-                        cls._update_arg_tuple_for_on_drop(arg_value)
-                        for arg_value in on_drop.args
-                    ),
-                )
             upload_props["on_drop"] = on_drop
             upload_props["on_drop"] = on_drop
 
 
         input_props_unique_name = get_unique_variable_name()
         input_props_unique_name = get_unique_variable_name()

+ 27 - 2
reflex/event.py

@@ -40,6 +40,7 @@ from reflex.utils.exceptions import (
 from reflex.utils.types import (
 from reflex.utils.types import (
     ArgsSpec,
     ArgsSpec,
     GenericType,
     GenericType,
+    Unset,
     safe_issubclass,
     safe_issubclass,
     typehint_issubclass,
     typehint_issubclass,
 )
 )
@@ -202,13 +203,14 @@ class EventHandler(EventActionsMixin):
         """
         """
         return getattr(self.fn, BACKGROUND_TASK_MARKER, False)
         return getattr(self.fn, BACKGROUND_TASK_MARKER, False)
 
 
-    def __call__(self, *args: Any) -> EventSpec:
+    def __call__(self, *args: Any, **kwargs: Any) -> EventSpec:
         """Pass arguments to the handler to get an event spec.
         """Pass arguments to the handler to get an event spec.
 
 
         This method configures event handlers that take in arguments.
         This method configures event handlers that take in arguments.
 
 
         Args:
         Args:
             *args: The arguments to pass to the handler.
             *args: The arguments to pass to the handler.
+            **kwargs: The keyword arguments to pass to the handler.
 
 
         Returns:
         Returns:
             The event spec, containing both the function and args.
             The event spec, containing both the function and args.
@@ -220,11 +222,34 @@ class EventHandler(EventActionsMixin):
 
 
         # Get the function args.
         # Get the function args.
         fn_args = list(inspect.signature(self.fn).parameters)[1:]
         fn_args = list(inspect.signature(self.fn).parameters)[1:]
+
+        if not isinstance(
+            repeated_arg := next(
+                (kwarg for kwarg in kwargs if kwarg in fn_args[: len(args)]), Unset()
+            ),
+            Unset,
+        ):
+            raise EventHandlerTypeError(
+                f"Event handler {self.fn.__name__} received repeated argument {repeated_arg}."
+            )
+
+        if not isinstance(
+            extra_arg := next(
+                (kwarg for kwarg in kwargs if kwarg not in fn_args), Unset()
+            ),
+            Unset,
+        ):
+            raise EventHandlerTypeError(
+                f"Event handler {self.fn.__name__} received extra argument {extra_arg}."
+            )
+
+        fn_args = fn_args[: len(args)] + list(kwargs)
+
         fn_args = (Var(_js_expr=arg) for arg in fn_args)
         fn_args = (Var(_js_expr=arg) for arg in fn_args)
 
 
         # Construct the payload.
         # Construct the payload.
         values = []
         values = []
-        for arg in args:
+        for arg in [*args, *kwargs.values()]:
             # Special case for file uploads.
             # Special case for file uploads.
             if isinstance(arg, FileUpload):
             if isinstance(arg, FileUpload):
                 return arg.as_event_spec(handler=self)
                 return arg.as_event_spec(handler=self)