fix: async default setters break setvar (#4169)

* fix: async default setters break setvar

* fix unit test
This commit is contained in:
benedikt-bartscher 2024-10-24 23:27:35 +02:00 committed by GitHub
parent d0c1eb7488
commit c0ed8b7d91
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 30 additions and 1 deletions

View File

@ -220,6 +220,7 @@ class EventHandlerSetVar(EventHandler):
Raises: Raises:
AttributeError: If the given Var name does not exist on the state. AttributeError: If the given Var name does not exist on the state.
EventHandlerValueError: If the given Var name is not a str EventHandlerValueError: If the given Var name is not a str
NotImplementedError: If the setter for the given Var is async
""" """
from reflex.utils.exceptions import EventHandlerValueError from reflex.utils.exceptions import EventHandlerValueError
@ -228,11 +229,20 @@ class EventHandlerSetVar(EventHandler):
raise EventHandlerValueError( raise EventHandlerValueError(
f"Var name must be passed as a string, got {args[0]!r}" f"Var name must be passed as a string, got {args[0]!r}"
) )
handler = getattr(self.state_cls, constants.SETTER_PREFIX + args[0], None)
# Check that the requested Var setter exists on the State at compile time. # Check that the requested Var setter exists on the State at compile time.
if getattr(self.state_cls, constants.SETTER_PREFIX + args[0], None) is None: if handler is None:
raise AttributeError( raise AttributeError(
f"Variable `{args[0]}` cannot be set on `{self.state_cls.get_full_name()}`" f"Variable `{args[0]}` cannot be set on `{self.state_cls.get_full_name()}`"
) )
if asyncio.iscoroutinefunction(handler.fn):
raise NotImplementedError(
f"Setter for {args[0]} is async, which is not supported."
)
return super().__call__(*args) return super().__call__(*args)

View File

@ -106,6 +106,7 @@ class TestState(BaseState):
fig: Figure = Figure() fig: Figure = Figure()
dt: datetime.datetime = datetime.datetime.fromisoformat("1989-11-09T18:53:00+01:00") dt: datetime.datetime = datetime.datetime.fromisoformat("1989-11-09T18:53:00+01:00")
_backend: int = 0 _backend: int = 0
asynctest: int = 0
@ComputedVar @ComputedVar
def sum(self) -> float: def sum(self) -> float:
@ -129,6 +130,14 @@ class TestState(BaseState):
"""Do something.""" """Do something."""
pass pass
async def set_asynctest(self, value: int):
"""Set the asynctest value. Intentionally overwrite the default setter with an async one.
Args:
value: The new value.
"""
self.asynctest = value
class ChildState(TestState): class ChildState(TestState):
"""A child state fixture.""" """A child state fixture."""
@ -313,6 +322,7 @@ def test_class_vars(test_state):
"upper", "upper",
"fig", "fig",
"dt", "dt",
"asynctest",
} }
@ -733,6 +743,7 @@ def test_reset(test_state, child_state):
"mapping", "mapping",
"dt", "dt",
"_backend", "_backend",
"asynctest",
} }
# The dirty vars should be reset. # The dirty vars should be reset.
@ -3179,6 +3190,13 @@ async def test_setvar(mock_app: rx.App, token: str):
TestState.setvar(42, 42) TestState.setvar(42, 42)
@pytest.mark.asyncio
async def test_setvar_async_setter():
"""Test that overridden async setters raise Exception when used with setvar."""
with pytest.raises(NotImplementedError):
TestState.setvar("asynctest", 42)
@pytest.mark.skipif("REDIS_URL" not in os.environ, reason="Test requires redis") @pytest.mark.skipif("REDIS_URL" not in os.environ, reason="Test requires redis")
@pytest.mark.parametrize( @pytest.mark.parametrize(
"expiration_kwargs, expected_values", "expiration_kwargs, expected_values",

View File

@ -601,6 +601,7 @@ formatted_router = {
"sum": 3.14, "sum": 3.14,
"upper": "", "upper": "",
"router": formatted_router, "router": formatted_router,
"asynctest": 0,
}, },
ChildState.get_full_name(): { ChildState.get_full_name(): {
"count": 23, "count": 23,