Skip to content

Commit

Permalink
fix: async default setters break setvar (#4169)
Browse files Browse the repository at this point in the history
* fix: async default setters break setvar

* fix unit test
  • Loading branch information
benedikt-bartscher authored Oct 24, 2024
1 parent d0c1eb7 commit c0ed8b7
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 1 deletion.
12 changes: 11 additions & 1 deletion reflex/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ def __call__(self, *args: Any) -> EventSpec:
Raises:
AttributeError: If the given Var name does not exist on the state.
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

Expand All @@ -228,11 +229,20 @@ def __call__(self, *args: Any) -> EventSpec:
raise EventHandlerValueError(
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.
if getattr(self.state_cls, constants.SETTER_PREFIX + args[0], None) is None:
if handler is None:
raise AttributeError(
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)


Expand Down
18 changes: 18 additions & 0 deletions tests/units/test_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ class TestState(BaseState):
fig: Figure = Figure()
dt: datetime.datetime = datetime.datetime.fromisoformat("1989-11-09T18:53:00+01:00")
_backend: int = 0
asynctest: int = 0

@ComputedVar
def sum(self) -> float:
Expand All @@ -129,6 +130,14 @@ def do_something(self):
"""Do something."""
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):
"""A child state fixture."""
Expand Down Expand Up @@ -313,6 +322,7 @@ def test_class_vars(test_state):
"upper",
"fig",
"dt",
"asynctest",
}


Expand Down Expand Up @@ -733,6 +743,7 @@ def test_reset(test_state, child_state):
"mapping",
"dt",
"_backend",
"asynctest",
}

# The dirty vars should be reset.
Expand Down Expand Up @@ -3179,6 +3190,13 @@ async def test_setvar(mock_app: rx.App, token: str):
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.parametrize(
"expiration_kwargs, expected_values",
Expand Down
1 change: 1 addition & 0 deletions tests/units/utils/test_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,7 @@ def test_format_query_params(input, output):
"sum": 3.14,
"upper": "",
"router": formatted_router,
"asynctest": 0,
},
ChildState.get_full_name(): {
"count": 23,
Expand Down

0 comments on commit c0ed8b7

Please sign in to comment.