diff --git a/packages/reflex-base/news/7397.misc.md b/packages/reflex-base/news/7397.misc.md new file mode 100644 index 00000000000..82277b2d6bf --- /dev/null +++ b/packages/reflex-base/news/7397.misc.md @@ -0,0 +1 @@ +`LambdaEventCallback.__code__` is now declared read-only, matching bound methods such as `ClientStateVar.set_value`, whose `__code__` cannot be assigned. diff --git a/packages/reflex-base/src/reflex_base/event/__init__.py b/packages/reflex-base/src/reflex_base/event/__init__.py index 7f6c6ffe43e..89f2e1bd446 100644 --- a/packages/reflex-base/src/reflex_base/event/__init__.py +++ b/packages/reflex-base/src/reflex_base/event/__init__.py @@ -3054,7 +3054,17 @@ def __get__(self, instance: Any, owner: Any) -> Callable: class LambdaEventCallback(Protocol[*P]): """A protocol for a lambda event callback.""" - __code__: types.CodeType + @property + def __code__(self) -> types.CodeType: + """The code object of the callback. + + Read-only, so a bound method, whose ``__code__`` is a read-only view of + its function's, satisfies the protocol as well as a function or lambda. + + Returns: + The code object. + """ + ... @overload def __call__(self: "LambdaEventCallback[()]") -> Any: ... diff --git a/tests/units/test_event.py b/tests/units/test_event.py index abc07d9a963..4c1a69b7be5 100644 --- a/tests/units/test_event.py +++ b/tests/units/test_event.py @@ -1,3 +1,4 @@ +import inspect import json import logging import shutil @@ -1293,6 +1294,19 @@ def return_plain_callable_var(_v: Var[Any]) -> Any: ) +def test_lambda_event_callback_only_requires_a_readable_code_object(): + """Bound methods expose a read-only ``__code__``, so the protocol must not need a settable one.""" + + class Handlers: + def on_value(self, value: str) -> None: ... + + assert Handlers().on_value.__code__ is Handlers.on_value.__code__ + + code = inspect.getattr_static(LambdaEventCallback, "__code__") + assert isinstance(code, property) + assert code.fset is None + + def test_event_chain_create_accepts_a_tuple_of_events(): """A tuple of events binds into the same chain as a list of them.""" events = (_handler_with_arg()(1), rx.console_log("logged"))