[ENG-3476] Setting State Vars that are not defined should raise an error (#4007)
This commit is contained in:
parent
12d73e4167
commit
4b3d056212
@ -74,6 +74,7 @@ from reflex.utils.exceptions import (
|
|||||||
EventHandlerShadowsBuiltInStateMethod,
|
EventHandlerShadowsBuiltInStateMethod,
|
||||||
ImmutableStateError,
|
ImmutableStateError,
|
||||||
LockExpiredError,
|
LockExpiredError,
|
||||||
|
SetUndefinedStateVarError,
|
||||||
)
|
)
|
||||||
from reflex.utils.exec import is_testing_env
|
from reflex.utils.exec import is_testing_env
|
||||||
from reflex.utils.serializers import serializer
|
from reflex.utils.serializers import serializer
|
||||||
@ -1260,6 +1261,9 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|||||||
Args:
|
Args:
|
||||||
name: The name of the attribute.
|
name: The name of the attribute.
|
||||||
value: The value of the attribute.
|
value: The value of the attribute.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
SetUndefinedStateVarError: If a value of a var is set without first defining it.
|
||||||
"""
|
"""
|
||||||
if isinstance(value, MutableProxy):
|
if isinstance(value, MutableProxy):
|
||||||
# unwrap proxy objects when assigning back to the state
|
# unwrap proxy objects when assigning back to the state
|
||||||
@ -1277,6 +1281,17 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|||||||
self._mark_dirty()
|
self._mark_dirty()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if (
|
||||||
|
name not in self.vars
|
||||||
|
and name not in self.get_skip_vars()
|
||||||
|
and not name.startswith("__")
|
||||||
|
and not name.startswith(f"_{type(self).__name__}__")
|
||||||
|
):
|
||||||
|
raise SetUndefinedStateVarError(
|
||||||
|
f"The state variable '{name}' has not been defined in '{type(self).__name__}'. "
|
||||||
|
f"All state variables must be declared before they can be set."
|
||||||
|
)
|
||||||
|
|
||||||
# Set the attribute.
|
# Set the attribute.
|
||||||
super().__setattr__(name, value)
|
super().__setattr__(name, value)
|
||||||
|
|
||||||
|
@ -115,3 +115,7 @@ class PrimitiveUnserializableToJSON(ReflexError, ValueError):
|
|||||||
|
|
||||||
class InvalidLifespanTaskType(ReflexError, TypeError):
|
class InvalidLifespanTaskType(ReflexError, TypeError):
|
||||||
"""Raised when an invalid task type is registered as a lifespan task."""
|
"""Raised when an invalid task type is registered as a lifespan task."""
|
||||||
|
|
||||||
|
|
||||||
|
class SetUndefinedStateVarError(ReflexError, AttributeError):
|
||||||
|
"""Raised when setting the value of a var without first declaring it."""
|
||||||
|
@ -41,6 +41,7 @@ from reflex.state import (
|
|||||||
)
|
)
|
||||||
from reflex.testing import chdir
|
from reflex.testing import chdir
|
||||||
from reflex.utils import format, prerequisites, types
|
from reflex.utils import format, prerequisites, types
|
||||||
|
from reflex.utils.exceptions import SetUndefinedStateVarError
|
||||||
from reflex.utils.format import json_dumps
|
from reflex.utils.format import json_dumps
|
||||||
from reflex.vars.base import ComputedVar, Var
|
from reflex.vars.base import ComputedVar, Var
|
||||||
from tests.units.states.mutation import MutableSQLAModel, MutableTestState
|
from tests.units.states.mutation import MutableSQLAModel, MutableTestState
|
||||||
@ -3262,3 +3263,45 @@ def test_child_mixin_state() -> None:
|
|||||||
|
|
||||||
assert "computed" in ChildUsesMixinState.inherited_vars
|
assert "computed" in ChildUsesMixinState.inherited_vars
|
||||||
assert "computed" not in ChildUsesMixinState.computed_vars
|
assert "computed" not in ChildUsesMixinState.computed_vars
|
||||||
|
|
||||||
|
|
||||||
|
def test_assignment_to_undeclared_vars():
|
||||||
|
"""Test that an attribute error is thrown when undeclared vars are set."""
|
||||||
|
|
||||||
|
class State(BaseState):
|
||||||
|
val: str
|
||||||
|
_val: str
|
||||||
|
__val: str # type: ignore
|
||||||
|
|
||||||
|
def handle_supported_regular_vars(self):
|
||||||
|
self.val = "no underscore"
|
||||||
|
self._val = "single leading underscore"
|
||||||
|
self.__val = "double leading undercore"
|
||||||
|
|
||||||
|
def handle_regular_var(self):
|
||||||
|
self.num = 5
|
||||||
|
|
||||||
|
def handle_backend_var(self):
|
||||||
|
self._num = 5
|
||||||
|
|
||||||
|
def handle_non_var(self):
|
||||||
|
self.__num = 5
|
||||||
|
|
||||||
|
class Substate(State):
|
||||||
|
def handle_var(self):
|
||||||
|
self.value = 20
|
||||||
|
|
||||||
|
state = State() # type: ignore
|
||||||
|
sub_state = Substate() # type: ignore
|
||||||
|
|
||||||
|
with pytest.raises(SetUndefinedStateVarError):
|
||||||
|
state.handle_regular_var()
|
||||||
|
|
||||||
|
with pytest.raises(SetUndefinedStateVarError):
|
||||||
|
sub_state.handle_var()
|
||||||
|
|
||||||
|
with pytest.raises(SetUndefinedStateVarError):
|
||||||
|
state.handle_backend_var()
|
||||||
|
|
||||||
|
state.handle_supported_regular_vars()
|
||||||
|
state.handle_non_var()
|
||||||
|
Loading…
Reference in New Issue
Block a user