소스 검색

Allow calling event handlers with keyword arguments (#5124)

* Allow calling event handlers with keyword arguments

* why is upload so weird
Khaleel Al-Adhami 1 개월 전
부모
커밋
ce661891af
3개의 변경된 파일33개의 추가작업 그리고 12개의 파일을 삭제
  1. 1 1
      pyi_hashes.json
  2. 5 9
      reflex/components/core/upload.py
  3. 27 2
      reflex/event.py

+ 1 - 1
pyi_hashes.json

@@ -20,7 +20,7 @@
   "reflex/components/core/debounce.pyi": "76d857eb814bc64625860a5f43e8b230",
   "reflex/components/core/html.pyi": "b12117b42ef79ee90b6b4dec50baeb86",
   "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/code.pyi": "3d8f0ab4c2f123d7f80d15c7ebc553d9",
   "reflex/components/datadisplay/dataeditor.pyi": "1b762071001161e4fdd1285263c33bb3",

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

@@ -23,6 +23,7 @@ from reflex.event import (
     EventHandler,
     EventSpec,
     call_event_fn,
+    call_event_handler,
     parse_args_spec,
     run_script,
 )
@@ -265,17 +266,12 @@ class Upload(MemoizationLeaf):
             upload_props["on_drop"] = upload_file(upload_props["id"])
         else:
             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.
                 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
 
         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 (
     ArgsSpec,
     GenericType,
+    Unset,
     safe_issubclass,
     typehint_issubclass,
 )
@@ -202,13 +203,14 @@ class EventHandler(EventActionsMixin):
         """
         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.
 
         This method configures event handlers that take in arguments.
 
         Args:
             *args: The arguments to pass to the handler.
+            **kwargs: The keyword arguments to pass to the handler.
 
         Returns:
             The event spec, containing both the function and args.
@@ -220,11 +222,34 @@ class EventHandler(EventActionsMixin):
 
         # Get the function args.
         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)
 
         # Construct the payload.
         values = []
-        for arg in args:
+        for arg in [*args, *kwargs.values()]:
             # Special case for file uploads.
             if isinstance(arg, FileUpload):
                 return arg.as_event_spec(handler=self)