[REF-2774] Add ReflexError and subclasses, send in telemetry (#3271)
This commit is contained in:
parent
4e959694a1
commit
2789f32134
@ -403,9 +403,12 @@ class App(Base):
|
|||||||
The generated component.
|
The generated component.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
|
VarOperationTypeError: When an invalid component var related function is passed.
|
||||||
TypeError: When an invalid component function is passed.
|
TypeError: When an invalid component function is passed.
|
||||||
exceptions.MatchTypeError: If the return types of match cases in rx.match are different.
|
exceptions.MatchTypeError: If the return types of match cases in rx.match are different.
|
||||||
"""
|
"""
|
||||||
|
from reflex.utils.exceptions import VarOperationTypeError
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return component if isinstance(component, Component) else component()
|
return component if isinstance(component, Component) else component()
|
||||||
except exceptions.MatchTypeError:
|
except exceptions.MatchTypeError:
|
||||||
@ -413,7 +416,7 @@ class App(Base):
|
|||||||
except TypeError as e:
|
except TypeError as e:
|
||||||
message = str(e)
|
message = str(e)
|
||||||
if "BaseVar" in message or "ComputedVar" in message:
|
if "BaseVar" in message or "ComputedVar" in message:
|
||||||
raise TypeError(
|
raise VarOperationTypeError(
|
||||||
"You may be trying to use an invalid Python function on a state var. "
|
"You may be trying to use an invalid Python function on a state var. "
|
||||||
"When referencing a var inside your render code, only limited var operations are supported. "
|
"When referencing a var inside your render code, only limited var operations are supported. "
|
||||||
"See the var operation docs here: https://reflex.dev/docs/vars/var-operations/"
|
"See the var operation docs here: https://reflex.dev/docs/vars/var-operations/"
|
||||||
@ -555,11 +558,13 @@ class App(Base):
|
|||||||
Based on conflicts that NextJS would throw if not intercepted.
|
Based on conflicts that NextJS would throw if not intercepted.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
ValueError: exception showing which conflict exist with the route to be added
|
RouteValueError: exception showing which conflict exist with the route to be added
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
new_route: the route being newly added.
|
new_route: the route being newly added.
|
||||||
"""
|
"""
|
||||||
|
from reflex.utils.exceptions import RouteValueError
|
||||||
|
|
||||||
if "[" not in new_route:
|
if "[" not in new_route:
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -576,7 +581,7 @@ class App(Base):
|
|||||||
):
|
):
|
||||||
if rw in segments and r != nr:
|
if rw in segments and r != nr:
|
||||||
# If the slugs in the segments of both routes are not the same, then the route is invalid
|
# If the slugs in the segments of both routes are not the same, then the route is invalid
|
||||||
raise ValueError(
|
raise RouteValueError(
|
||||||
f"You cannot use different slug names for the same dynamic path in {route} and {new_route} ('{r}' != '{nr}')"
|
f"You cannot use different slug names for the same dynamic path in {route} and {new_route} ('{r}' != '{nr}')"
|
||||||
)
|
)
|
||||||
elif rw not in segments and r != nr:
|
elif rw not in segments and r != nr:
|
||||||
@ -755,8 +760,10 @@ class App(Base):
|
|||||||
export: Whether to compile the app for export.
|
export: Whether to compile the app for export.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
RuntimeError: When any page uses state, but no rx.State subclass is defined.
|
ReflexRuntimeError: When any page uses state, but no rx.State subclass is defined.
|
||||||
"""
|
"""
|
||||||
|
from reflex.utils.exceptions import ReflexRuntimeError
|
||||||
|
|
||||||
# Render a default 404 page if the user didn't supply one
|
# Render a default 404 page if the user didn't supply one
|
||||||
if constants.Page404.SLUG not in self.pages:
|
if constants.Page404.SLUG not in self.pages:
|
||||||
self.add_custom_404_page()
|
self.add_custom_404_page()
|
||||||
@ -837,7 +844,7 @@ class App(Base):
|
|||||||
|
|
||||||
# Catch "static" apps (that do not define a rx.State subclass) which are trying to access rx.State.
|
# Catch "static" apps (that do not define a rx.State subclass) which are trying to access rx.State.
|
||||||
if code_uses_state_contexts(stateful_components_code) and self.state is None:
|
if code_uses_state_contexts(stateful_components_code) and self.state is None:
|
||||||
raise RuntimeError(
|
raise ReflexRuntimeError(
|
||||||
"To access rx.State in frontend components, at least one "
|
"To access rx.State in frontend components, at least one "
|
||||||
"subclass of rx.State must be defined in the app."
|
"subclass of rx.State must be defined in the app."
|
||||||
)
|
)
|
||||||
@ -1137,10 +1144,12 @@ def upload(app: App):
|
|||||||
emitted by the upload handler.
|
emitted by the upload handler.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
ValueError: if there are no args with supported annotation.
|
UploadValueError: if there are no args with supported annotation.
|
||||||
TypeError: if a background task is used as the handler.
|
UploadTypeError: if a background task is used as the handler.
|
||||||
HTTPException: when the request does not include token / handler headers.
|
HTTPException: when the request does not include token / handler headers.
|
||||||
"""
|
"""
|
||||||
|
from reflex.utils.exceptions import UploadTypeError, UploadValueError
|
||||||
|
|
||||||
token = request.headers.get("reflex-client-token")
|
token = request.headers.get("reflex-client-token")
|
||||||
handler = request.headers.get("reflex-event-handler")
|
handler = request.headers.get("reflex-event-handler")
|
||||||
|
|
||||||
@ -1166,7 +1175,7 @@ def upload(app: App):
|
|||||||
# check if there exists any handler args with annotation, List[UploadFile]
|
# check if there exists any handler args with annotation, List[UploadFile]
|
||||||
if isinstance(func, EventHandler):
|
if isinstance(func, EventHandler):
|
||||||
if func.is_background:
|
if func.is_background:
|
||||||
raise TypeError(
|
raise UploadTypeError(
|
||||||
f"@rx.background is not supported for upload handler `{handler}`.",
|
f"@rx.background is not supported for upload handler `{handler}`.",
|
||||||
)
|
)
|
||||||
func = func.fn
|
func = func.fn
|
||||||
@ -1181,7 +1190,7 @@ def upload(app: App):
|
|||||||
break
|
break
|
||||||
|
|
||||||
if not handler_upload_param:
|
if not handler_upload_param:
|
||||||
raise ValueError(
|
raise UploadValueError(
|
||||||
f"`{handler}` handler should have a parameter annotated as "
|
f"`{handler}` handler should have a parameter annotated as "
|
||||||
"List[rx.UploadFile]"
|
"List[rx.UploadFile]"
|
||||||
)
|
)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
"""Define the base Reflex class."""
|
"""Define the base Reflex class."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
@ -26,15 +27,17 @@ def validate_field_name(bases: List[Type["BaseModel"]], field_name: str) -> None
|
|||||||
field_name: name of attribute
|
field_name: name of attribute
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
NameError: If state var field shadows another in its parent state
|
VarNameError: If state var field shadows another in its parent state
|
||||||
"""
|
"""
|
||||||
|
from reflex.utils.exceptions import VarNameError
|
||||||
|
|
||||||
reload = os.getenv(constants.RELOAD_CONFIG) == "True"
|
reload = os.getenv(constants.RELOAD_CONFIG) == "True"
|
||||||
for base in bases:
|
for base in bases:
|
||||||
try:
|
try:
|
||||||
if not reload and getattr(base, field_name, None):
|
if not reload and getattr(base, field_name, None):
|
||||||
pass
|
pass
|
||||||
except TypeError as te:
|
except TypeError as te:
|
||||||
raise NameError(
|
raise VarNameError(
|
||||||
f'State var "{field_name}" in {base} has been shadowed by a substate var; '
|
f'State var "{field_name}" in {base} has been shadowed by a substate var; '
|
||||||
f'use a different field name instead".'
|
f'use a different field name instead".'
|
||||||
) from te
|
) from te
|
||||||
|
@ -731,6 +731,7 @@ class Component(BaseComponent, ABC):
|
|||||||
# Import here to avoid circular imports.
|
# Import here to avoid circular imports.
|
||||||
from reflex.components.base.bare import Bare
|
from reflex.components.base.bare import Bare
|
||||||
from reflex.components.base.fragment import Fragment
|
from reflex.components.base.fragment import Fragment
|
||||||
|
from reflex.utils.exceptions import ComponentTypeError
|
||||||
|
|
||||||
# Translate deprecated props to new names.
|
# Translate deprecated props to new names.
|
||||||
new_prop_names = [
|
new_prop_names = [
|
||||||
@ -757,7 +758,7 @@ class Component(BaseComponent, ABC):
|
|||||||
validate_children(child)
|
validate_children(child)
|
||||||
# Make sure the child is a valid type.
|
# Make sure the child is a valid type.
|
||||||
if not types._isinstance(child, ComponentChild):
|
if not types._isinstance(child, ComponentChild):
|
||||||
raise TypeError(
|
raise ComponentTypeError(
|
||||||
"Children of Reflex components must be other components, "
|
"Children of Reflex components must be other components, "
|
||||||
"state vars, or primitive Python types. "
|
"state vars, or primitive Python types. "
|
||||||
f"Got child {child} of type {type(child)}.",
|
f"Got child {child} of type {type(child)}.",
|
||||||
|
@ -251,8 +251,10 @@ class Config(Base):
|
|||||||
The updated config values.
|
The updated config values.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
ValueError: If an environment variable is set to an invalid type.
|
EnvVarValueError: If an environment variable is set to an invalid type.
|
||||||
"""
|
"""
|
||||||
|
from reflex.utils.exceptions import EnvVarValueError
|
||||||
|
|
||||||
updated_values = {}
|
updated_values = {}
|
||||||
# Iterate over the fields.
|
# Iterate over the fields.
|
||||||
for key, field in self.__fields__.items():
|
for key, field in self.__fields__.items():
|
||||||
@ -273,11 +275,11 @@ class Config(Base):
|
|||||||
env_var = env_var.lower() in ["true", "1", "yes"]
|
env_var = env_var.lower() in ["true", "1", "yes"]
|
||||||
else:
|
else:
|
||||||
env_var = field.type_(env_var)
|
env_var = field.type_(env_var)
|
||||||
except ValueError:
|
except ValueError as ve:
|
||||||
console.error(
|
console.error(
|
||||||
f"Could not convert {key.upper()}={env_var} to type {field.type_}"
|
f"Could not convert {key.upper()}={env_var} to type {field.type_}"
|
||||||
)
|
)
|
||||||
raise
|
raise EnvVarValueError from ve
|
||||||
|
|
||||||
# Set the value.
|
# Set the value.
|
||||||
updated_values[key] = env_var
|
updated_values[key] = env_var
|
||||||
|
@ -180,8 +180,10 @@ class EventHandler(EventActionsMixin):
|
|||||||
The event spec, containing both the function and args.
|
The event spec, containing both the function and args.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
TypeError: If the arguments are invalid.
|
EventHandlerTypeError: If the arguments are invalid.
|
||||||
"""
|
"""
|
||||||
|
from reflex.utils.exceptions import EventHandlerTypeError
|
||||||
|
|
||||||
# Get the function args.
|
# Get the function args.
|
||||||
fn_args = inspect.getfullargspec(self.fn).args[1:]
|
fn_args = inspect.getfullargspec(self.fn).args[1:]
|
||||||
fn_args = (Var.create_safe(arg) for arg in fn_args)
|
fn_args = (Var.create_safe(arg) for arg in fn_args)
|
||||||
@ -197,7 +199,7 @@ class EventHandler(EventActionsMixin):
|
|||||||
try:
|
try:
|
||||||
values.append(Var.create(arg, _var_is_string=isinstance(arg, str)))
|
values.append(Var.create(arg, _var_is_string=isinstance(arg, str)))
|
||||||
except TypeError as e:
|
except TypeError as e:
|
||||||
raise TypeError(
|
raise EventHandlerTypeError(
|
||||||
f"Arguments to event handlers must be Vars or JSON-serializable. Got {arg} of type {type(arg)}."
|
f"Arguments to event handlers must be Vars or JSON-serializable. Got {arg} of type {type(arg)}."
|
||||||
) from e
|
) from e
|
||||||
payload = tuple(zip(fn_args, values))
|
payload = tuple(zip(fn_args, values))
|
||||||
@ -256,8 +258,10 @@ class EventSpec(EventActionsMixin):
|
|||||||
The event spec with the new arguments.
|
The event spec with the new arguments.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
TypeError: If the arguments are invalid.
|
EventHandlerTypeError: If the arguments are invalid.
|
||||||
"""
|
"""
|
||||||
|
from reflex.utils.exceptions import EventHandlerTypeError
|
||||||
|
|
||||||
# Get the remaining unfilled function args.
|
# Get the remaining unfilled function args.
|
||||||
fn_args = inspect.getfullargspec(self.handler.fn).args[1 + len(self.args) :]
|
fn_args = inspect.getfullargspec(self.handler.fn).args[1 + len(self.args) :]
|
||||||
fn_args = (Var.create_safe(arg) for arg in fn_args)
|
fn_args = (Var.create_safe(arg) for arg in fn_args)
|
||||||
@ -268,7 +272,7 @@ class EventSpec(EventActionsMixin):
|
|||||||
try:
|
try:
|
||||||
values.append(Var.create(arg, _var_is_string=isinstance(arg, str)))
|
values.append(Var.create(arg, _var_is_string=isinstance(arg, str)))
|
||||||
except TypeError as e:
|
except TypeError as e:
|
||||||
raise TypeError(
|
raise EventHandlerTypeError(
|
||||||
f"Arguments to event handlers must be Vars or JSON-serializable. Got {arg} of type {type(arg)}."
|
f"Arguments to event handlers must be Vars or JSON-serializable. Got {arg} of type {type(arg)}."
|
||||||
) from e
|
) from e
|
||||||
new_payload = tuple(zip(fn_args, values))
|
new_payload = tuple(zip(fn_args, values))
|
||||||
@ -312,10 +316,12 @@ class CallableEventSpec(EventSpec):
|
|||||||
The EventSpec returned from calling the function.
|
The EventSpec returned from calling the function.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
TypeError: If the CallableEventSpec has no associated function.
|
EventHandlerTypeError: If the CallableEventSpec has no associated function.
|
||||||
"""
|
"""
|
||||||
|
from reflex.utils.exceptions import EventHandlerTypeError
|
||||||
|
|
||||||
if self.fn is None:
|
if self.fn is None:
|
||||||
raise TypeError("CallableEventSpec has no associated function.")
|
raise EventHandlerTypeError("CallableEventSpec has no associated function.")
|
||||||
return self.fn(*args, **kwargs)
|
return self.fn(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
@ -834,10 +840,11 @@ def call_event_fn(fn: Callable, arg: Union[Var, ArgsSpec]) -> list[EventSpec]:
|
|||||||
The event specs from calling the function.
|
The event specs from calling the function.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
ValueError: If the lambda has an invalid signature.
|
EventHandlerValueError: If the lambda has an invalid signature.
|
||||||
"""
|
"""
|
||||||
# Import here to avoid circular imports.
|
# Import here to avoid circular imports.
|
||||||
from reflex.event import EventHandler, EventSpec
|
from reflex.event import EventHandler, EventSpec
|
||||||
|
from reflex.utils.exceptions import EventHandlerValueError
|
||||||
|
|
||||||
# Get the args of the lambda.
|
# Get the args of the lambda.
|
||||||
args = inspect.getfullargspec(fn).args
|
args = inspect.getfullargspec(fn).args
|
||||||
@ -851,7 +858,7 @@ def call_event_fn(fn: Callable, arg: Union[Var, ArgsSpec]) -> list[EventSpec]:
|
|||||||
elif len(args) == 1:
|
elif len(args) == 1:
|
||||||
out = fn(arg)
|
out = fn(arg)
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Lambda {fn} must have 0 or 1 arguments.")
|
raise EventHandlerValueError(f"Lambda {fn} must have 0 or 1 arguments.")
|
||||||
|
|
||||||
# Convert the output to a list.
|
# Convert the output to a list.
|
||||||
if not isinstance(out, List):
|
if not isinstance(out, List):
|
||||||
@ -869,7 +876,9 @@ def call_event_fn(fn: Callable, arg: Union[Var, ArgsSpec]) -> list[EventSpec]:
|
|||||||
|
|
||||||
# Make sure the event spec is valid.
|
# Make sure the event spec is valid.
|
||||||
if not isinstance(e, EventSpec):
|
if not isinstance(e, EventSpec):
|
||||||
raise ValueError(f"Lambda {fn} returned an invalid event spec: {e}.")
|
raise EventHandlerValueError(
|
||||||
|
f"Lambda {fn} returned an invalid event spec: {e}."
|
||||||
|
)
|
||||||
|
|
||||||
# Add the event spec to the chain.
|
# Add the event spec to the chain.
|
||||||
events.append(e)
|
events.append(e)
|
||||||
|
@ -279,11 +279,13 @@ 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.
|
||||||
ValueError: If the given Var name is not a str
|
EventHandlerValueError: If the given Var name is not a str
|
||||||
"""
|
"""
|
||||||
|
from reflex.utils.exceptions import EventHandlerValueError
|
||||||
|
|
||||||
if args:
|
if args:
|
||||||
if not isinstance(args[0], str):
|
if not isinstance(args[0], str):
|
||||||
raise ValueError(
|
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}"
|
||||||
)
|
)
|
||||||
# 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.
|
||||||
@ -380,10 +382,12 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|||||||
**kwargs: The kwargs to pass to the Pydantic init method.
|
**kwargs: The kwargs to pass to the Pydantic init method.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
RuntimeError: If the state is instantiated directly by end user.
|
ReflexRuntimeError: If the state is instantiated directly by end user.
|
||||||
"""
|
"""
|
||||||
|
from reflex.utils.exceptions import ReflexRuntimeError
|
||||||
|
|
||||||
if not _reflex_internal_init and not is_testing_env():
|
if not _reflex_internal_init and not is_testing_env():
|
||||||
raise RuntimeError(
|
raise ReflexRuntimeError(
|
||||||
"State classes should not be instantiated directly in a Reflex app. "
|
"State classes should not be instantiated directly in a Reflex app. "
|
||||||
"See https://reflex.dev/docs/state/ for further information."
|
"See https://reflex.dev/docs/state/ for further information."
|
||||||
)
|
)
|
||||||
@ -438,8 +442,10 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|||||||
**kwargs: The kwargs to pass to the pydantic init_subclass method.
|
**kwargs: The kwargs to pass to the pydantic init_subclass method.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
ValueError: If a substate class shadows another.
|
StateValueError: If a substate class shadows another.
|
||||||
"""
|
"""
|
||||||
|
from reflex.utils.exceptions import StateValueError
|
||||||
|
|
||||||
super().__init_subclass__(**kwargs)
|
super().__init_subclass__(**kwargs)
|
||||||
# Event handlers should not shadow builtin state methods.
|
# Event handlers should not shadow builtin state methods.
|
||||||
cls._check_overridden_methods()
|
cls._check_overridden_methods()
|
||||||
@ -471,7 +477,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|||||||
else:
|
else:
|
||||||
# During normal operation, subclasses cannot have the same name, even if they are
|
# During normal operation, subclasses cannot have the same name, even if they are
|
||||||
# defined in different modules.
|
# defined in different modules.
|
||||||
raise ValueError(
|
raise StateValueError(
|
||||||
f"The substate class '{cls.__name__}' has been defined multiple times. "
|
f"The substate class '{cls.__name__}' has been defined multiple times. "
|
||||||
"Shadowing substate classes is not allowed."
|
"Shadowing substate classes is not allowed."
|
||||||
)
|
)
|
||||||
@ -829,10 +835,12 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|||||||
prop: The variable to initialize
|
prop: The variable to initialize
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
TypeError: if the variable has an incorrect type
|
VarTypeError: if the variable has an incorrect type
|
||||||
"""
|
"""
|
||||||
|
from reflex.utils.exceptions import VarTypeError
|
||||||
|
|
||||||
if not types.is_valid_var_type(prop._var_type):
|
if not types.is_valid_var_type(prop._var_type):
|
||||||
raise TypeError(
|
raise VarTypeError(
|
||||||
"State vars must be primitive Python types, "
|
"State vars must be primitive Python types, "
|
||||||
"Plotly figures, Pandas dataframes, "
|
"Plotly figures, Pandas dataframes, "
|
||||||
"or subclasses of rx.Base. "
|
"or subclasses of rx.Base. "
|
||||||
@ -1688,10 +1696,12 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|||||||
if initial:
|
if initial:
|
||||||
computed_vars = {
|
computed_vars = {
|
||||||
# Include initial computed vars.
|
# Include initial computed vars.
|
||||||
prop_name: cv._initial_value
|
prop_name: (
|
||||||
if isinstance(cv, ComputedVar)
|
cv._initial_value
|
||||||
and not isinstance(cv._initial_value, types.Unset)
|
if isinstance(cv, ComputedVar)
|
||||||
else self.get_value(getattr(self, prop_name))
|
and not isinstance(cv._initial_value, types.Unset)
|
||||||
|
else self.get_value(getattr(self, prop_name))
|
||||||
|
)
|
||||||
for prop_name, cv in self.computed_vars.items()
|
for prop_name, cv in self.computed_vars.items()
|
||||||
}
|
}
|
||||||
elif include_computed:
|
elif include_computed:
|
||||||
|
@ -1,21 +1,77 @@
|
|||||||
"""Custom Exceptions."""
|
"""Custom Exceptions."""
|
||||||
|
|
||||||
|
|
||||||
class InvalidStylePropError(TypeError):
|
class ReflexError(Exception):
|
||||||
|
"""Base exception for all Reflex exceptions."""
|
||||||
|
|
||||||
|
|
||||||
|
class ReflexRuntimeError(ReflexError, RuntimeError):
|
||||||
|
"""Custom RuntimeError for Reflex."""
|
||||||
|
|
||||||
|
|
||||||
|
class UploadTypeError(ReflexError, TypeError):
|
||||||
|
"""Custom TypeError for upload related errors."""
|
||||||
|
|
||||||
|
|
||||||
|
class EnvVarValueError(ReflexError, ValueError):
|
||||||
|
"""Custom ValueError raised when unable to convert env var to expected type."""
|
||||||
|
|
||||||
|
|
||||||
|
class ComponentTypeError(ReflexError, TypeError):
|
||||||
|
"""Custom TypeError for component related errors."""
|
||||||
|
|
||||||
|
|
||||||
|
class EventHandlerTypeError(ReflexError, TypeError):
|
||||||
|
"""Custom TypeError for event handler related errors."""
|
||||||
|
|
||||||
|
|
||||||
|
class EventHandlerValueError(ReflexError, ValueError):
|
||||||
|
"""Custom ValueError for event handler related errors."""
|
||||||
|
|
||||||
|
|
||||||
|
class StateValueError(ReflexError, ValueError):
|
||||||
|
"""Custom ValueError for state related errors."""
|
||||||
|
|
||||||
|
|
||||||
|
class VarNameError(ReflexError, NameError):
|
||||||
|
"""Custom NameError for when a state var has been shadowed by a substate var."""
|
||||||
|
|
||||||
|
|
||||||
|
class VarTypeError(ReflexError, TypeError):
|
||||||
|
"""Custom TypeError for var related errors."""
|
||||||
|
|
||||||
|
|
||||||
|
class VarValueError(ReflexError, ValueError):
|
||||||
|
"""Custom ValueError for var related errors."""
|
||||||
|
|
||||||
|
|
||||||
|
class VarAttributeError(ReflexError, AttributeError):
|
||||||
|
"""Custom AttributeError for var related errors."""
|
||||||
|
|
||||||
|
|
||||||
|
class UploadValueError(ReflexError, ValueError):
|
||||||
|
"""Custom ValueError for upload related errors."""
|
||||||
|
|
||||||
|
|
||||||
|
class RouteValueError(ReflexError, ValueError):
|
||||||
|
"""Custom ValueError for route related errors."""
|
||||||
|
|
||||||
|
|
||||||
|
class VarOperationTypeError(ReflexError, TypeError):
|
||||||
|
"""Custom TypeError for when unsupported operations are performed on vars."""
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidStylePropError(ReflexError, TypeError):
|
||||||
"""Custom Type Error when style props have invalid values."""
|
"""Custom Type Error when style props have invalid values."""
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
class ImmutableStateError(ReflexError):
|
||||||
class ImmutableStateError(AttributeError):
|
|
||||||
"""Raised when a background task attempts to modify state outside of context."""
|
"""Raised when a background task attempts to modify state outside of context."""
|
||||||
|
|
||||||
|
|
||||||
class LockExpiredError(Exception):
|
class LockExpiredError(ReflexError):
|
||||||
"""Raised when the state lock expires while an event is being processed."""
|
"""Raised when the state lock expires while an event is being processed."""
|
||||||
|
|
||||||
|
|
||||||
class MatchTypeError(TypeError):
|
class MatchTypeError(ReflexError, TypeError):
|
||||||
"""Raised when the return types of match cases are different."""
|
"""Raised when the return types of match cases are different."""
|
||||||
|
|
||||||
pass
|
|
||||||
|
@ -210,28 +210,35 @@ def get_app(reload: bool = False) -> ModuleType:
|
|||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
RuntimeError: If the app name is not set in the config.
|
RuntimeError: If the app name is not set in the config.
|
||||||
|
exceptions.ReflexError: Reflex specific errors.
|
||||||
"""
|
"""
|
||||||
os.environ[constants.RELOAD_CONFIG] = str(reload)
|
from reflex.utils import exceptions, telemetry
|
||||||
config = get_config()
|
|
||||||
if not config.app_name:
|
|
||||||
raise RuntimeError(
|
|
||||||
"Cannot get the app module because `app_name` is not set in rxconfig! "
|
|
||||||
"If this error occurs in a reflex test case, ensure that `get_app` is mocked."
|
|
||||||
)
|
|
||||||
module = config.module
|
|
||||||
sys.path.insert(0, os.getcwd())
|
|
||||||
app = __import__(module, fromlist=(constants.CompileVars.APP,))
|
|
||||||
|
|
||||||
if reload:
|
try:
|
||||||
from reflex.state import reload_state_module
|
os.environ[constants.RELOAD_CONFIG] = str(reload)
|
||||||
|
config = get_config()
|
||||||
|
if not config.app_name:
|
||||||
|
raise RuntimeError(
|
||||||
|
"Cannot get the app module because `app_name` is not set in rxconfig! "
|
||||||
|
"If this error occurs in a reflex test case, ensure that `get_app` is mocked."
|
||||||
|
)
|
||||||
|
module = config.module
|
||||||
|
sys.path.insert(0, os.getcwd())
|
||||||
|
app = __import__(module, fromlist=(constants.CompileVars.APP,))
|
||||||
|
|
||||||
# Reset rx.State subclasses to avoid conflict when reloading.
|
if reload:
|
||||||
reload_state_module(module=module)
|
from reflex.state import reload_state_module
|
||||||
|
|
||||||
# Reload the app module.
|
# Reset rx.State subclasses to avoid conflict when reloading.
|
||||||
importlib.reload(app)
|
reload_state_module(module=module)
|
||||||
|
|
||||||
return app
|
# Reload the app module.
|
||||||
|
importlib.reload(app)
|
||||||
|
|
||||||
|
return app
|
||||||
|
except exceptions.ReflexError as ex:
|
||||||
|
telemetry.send("error", context="frontend", detail=str(ex))
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
def get_compiled_app(reload: bool = False, export: bool = False) -> ModuleType:
|
def get_compiled_app(reload: bool = False, export: bool = False) -> ModuleType:
|
||||||
|
107
reflex/vars.py
107
reflex/vars.py
@ -35,6 +35,7 @@ from typing import (
|
|||||||
from reflex import constants
|
from reflex import constants
|
||||||
from reflex.base import Base
|
from reflex.base import Base
|
||||||
from reflex.utils import console, format, imports, serializers, types
|
from reflex.utils import console, format, imports, serializers, types
|
||||||
|
from reflex.utils.exceptions import VarAttributeError, VarTypeError, VarValueError
|
||||||
|
|
||||||
# This module used to export ImportVar itself, so we still import it for export here
|
# This module used to export ImportVar itself, so we still import it for export here
|
||||||
from reflex.utils.imports import ImportDict, ImportVar
|
from reflex.utils.imports import ImportDict, ImportVar
|
||||||
@ -353,7 +354,7 @@ class Var:
|
|||||||
The var.
|
The var.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
TypeError: If the value is JSON-unserializable.
|
VarTypeError: If the value is JSON-unserializable.
|
||||||
"""
|
"""
|
||||||
# Check for none values.
|
# Check for none values.
|
||||||
if value is None:
|
if value is None:
|
||||||
@ -372,7 +373,7 @@ class Var:
|
|||||||
type_ = type(value)
|
type_ = type(value)
|
||||||
name = value if type_ in types.JSONType else serializers.serialize(value)
|
name = value if type_ in types.JSONType else serializers.serialize(value)
|
||||||
if name is None:
|
if name is None:
|
||||||
raise TypeError(
|
raise VarTypeError(
|
||||||
f"No JSON serializer found for var {value} of type {type_}."
|
f"No JSON serializer found for var {value} of type {type_}."
|
||||||
)
|
)
|
||||||
name = name if isinstance(name, str) else format.json_dumps(name)
|
name = name if isinstance(name, str) else format.json_dumps(name)
|
||||||
@ -542,9 +543,9 @@ class Var:
|
|||||||
"""Raise exception if using Var in a boolean context.
|
"""Raise exception if using Var in a boolean context.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
TypeError: when attempting to bool-ify the Var.
|
VarTypeError: when attempting to bool-ify the Var.
|
||||||
"""
|
"""
|
||||||
raise TypeError(
|
raise VarTypeError(
|
||||||
f"Cannot convert Var {self._var_full_name!r} to bool for use with `if`, `and`, `or`, and `not`. "
|
f"Cannot convert Var {self._var_full_name!r} to bool for use with `if`, `and`, `or`, and `not`. "
|
||||||
"Instead use `rx.cond` and bitwise operators `&` (and), `|` (or), `~` (invert)."
|
"Instead use `rx.cond` and bitwise operators `&` (and), `|` (or), `~` (invert)."
|
||||||
)
|
)
|
||||||
@ -553,9 +554,9 @@ class Var:
|
|||||||
"""Raise exception if using Var in an iterable context.
|
"""Raise exception if using Var in an iterable context.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
TypeError: when attempting to iterate over the Var.
|
VarTypeError: when attempting to iterate over the Var.
|
||||||
"""
|
"""
|
||||||
raise TypeError(
|
raise VarTypeError(
|
||||||
f"Cannot iterate over Var {self._var_full_name!r}. Instead use `rx.foreach`."
|
f"Cannot iterate over Var {self._var_full_name!r}. Instead use `rx.foreach`."
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -584,7 +585,7 @@ class Var:
|
|||||||
The indexed var.
|
The indexed var.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
TypeError: If the var is not indexable.
|
VarTypeError: If the var is not indexable.
|
||||||
"""
|
"""
|
||||||
# Indexing is only supported for strings, lists, tuples, dicts, and dataframes.
|
# Indexing is only supported for strings, lists, tuples, dicts, and dataframes.
|
||||||
if not (
|
if not (
|
||||||
@ -592,11 +593,11 @@ class Var:
|
|||||||
or types.is_dataframe(self._var_type)
|
or types.is_dataframe(self._var_type)
|
||||||
):
|
):
|
||||||
if self._var_type == Any:
|
if self._var_type == Any:
|
||||||
raise TypeError(
|
raise VarTypeError(
|
||||||
"Could not index into var of type Any. (If you are trying to index into a state var, "
|
"Could not index into var of type Any. (If you are trying to index into a state var, "
|
||||||
"add the correct type annotation to the var.)"
|
"add the correct type annotation to the var.)"
|
||||||
)
|
)
|
||||||
raise TypeError(
|
raise VarTypeError(
|
||||||
f"Var {self._var_name} of type {self._var_type} does not support indexing."
|
f"Var {self._var_name} of type {self._var_type} does not support indexing."
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -615,7 +616,7 @@ class Var:
|
|||||||
or isinstance(i, Var)
|
or isinstance(i, Var)
|
||||||
and not i._var_type == int
|
and not i._var_type == int
|
||||||
):
|
):
|
||||||
raise TypeError("Index must be an integer or an integer var.")
|
raise VarTypeError("Index must be an integer or an integer var.")
|
||||||
|
|
||||||
# Handle slices first.
|
# Handle slices first.
|
||||||
if isinstance(i, slice):
|
if isinstance(i, slice):
|
||||||
@ -658,7 +659,7 @@ class Var:
|
|||||||
i._var_type, types.get_args(Union[int, str, float])
|
i._var_type, types.get_args(Union[int, str, float])
|
||||||
)
|
)
|
||||||
):
|
):
|
||||||
raise TypeError(
|
raise VarTypeError(
|
||||||
"Index must be one of the following types: int, str, int or str Var"
|
"Index must be one of the following types: int, str, int or str Var"
|
||||||
)
|
)
|
||||||
# Get the type of the indexed var.
|
# Get the type of the indexed var.
|
||||||
@ -687,7 +688,7 @@ class Var:
|
|||||||
The var attribute.
|
The var attribute.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
AttributeError: If the attribute cannot be found, or if __getattr__ fallback should be used.
|
VarAttributeError: If the attribute cannot be found, or if __getattr__ fallback should be used.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
var_attribute = super().__getattribute__(name)
|
var_attribute = super().__getattribute__(name)
|
||||||
@ -698,10 +699,12 @@ class Var:
|
|||||||
super().__getattribute__("_var_type"), name
|
super().__getattribute__("_var_type"), name
|
||||||
)
|
)
|
||||||
if type_ is not None:
|
if type_ is not None:
|
||||||
raise AttributeError(f"{name} is being accessed through the Var.")
|
raise VarAttributeError(
|
||||||
|
f"{name} is being accessed through the Var."
|
||||||
|
)
|
||||||
# Return the attribute as-is.
|
# Return the attribute as-is.
|
||||||
return var_attribute
|
return var_attribute
|
||||||
except AttributeError:
|
except VarAttributeError:
|
||||||
raise # fall back to __getattr__ anyway
|
raise # fall back to __getattr__ anyway
|
||||||
|
|
||||||
def __getattr__(self, name: str) -> Var:
|
def __getattr__(self, name: str) -> Var:
|
||||||
@ -714,13 +717,13 @@ class Var:
|
|||||||
The var attribute.
|
The var attribute.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
AttributeError: If the var is wrongly annotated or can't find attribute.
|
VarAttributeError: If the var is wrongly annotated or can't find attribute.
|
||||||
TypeError: If an annotation to the var isn't provided.
|
VarTypeError: If an annotation to the var isn't provided.
|
||||||
"""
|
"""
|
||||||
# Check if the attribute is one of the class fields.
|
# Check if the attribute is one of the class fields.
|
||||||
if not name.startswith("_"):
|
if not name.startswith("_"):
|
||||||
if self._var_type == Any:
|
if self._var_type == Any:
|
||||||
raise TypeError(
|
raise VarTypeError(
|
||||||
f"You must provide an annotation for the state var `{self._var_full_name}`. Annotation cannot be `{self._var_type}`"
|
f"You must provide an annotation for the state var `{self._var_full_name}`. Annotation cannot be `{self._var_type}`"
|
||||||
) from None
|
) from None
|
||||||
is_optional = types.is_optional(self._var_type)
|
is_optional = types.is_optional(self._var_type)
|
||||||
@ -734,16 +737,16 @@ class Var:
|
|||||||
)
|
)
|
||||||
|
|
||||||
if name in REPLACED_NAMES:
|
if name in REPLACED_NAMES:
|
||||||
raise AttributeError(
|
raise VarAttributeError(
|
||||||
f"Field {name!r} was renamed to {REPLACED_NAMES[name]!r}"
|
f"Field {name!r} was renamed to {REPLACED_NAMES[name]!r}"
|
||||||
)
|
)
|
||||||
|
|
||||||
raise AttributeError(
|
raise VarAttributeError(
|
||||||
f"The State var `{self._var_full_name}` has no attribute '{name}' or may have been annotated "
|
f"The State var `{self._var_full_name}` has no attribute '{name}' or may have been annotated "
|
||||||
f"wrongly."
|
f"wrongly."
|
||||||
)
|
)
|
||||||
|
|
||||||
raise AttributeError(
|
raise VarAttributeError(
|
||||||
f"The State var has no attribute '{name}' or may have been annotated wrongly.",
|
f"The State var has no attribute '{name}' or may have been annotated wrongly.",
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -770,8 +773,8 @@ class Var:
|
|||||||
The operation result.
|
The operation result.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
TypeError: If the operation between two operands is invalid.
|
VarTypeError: If the operation between two operands is invalid.
|
||||||
ValueError: If flip is set to true and value of operand is not provided
|
VarValueError: If flip is set to true and value of operand is not provided
|
||||||
"""
|
"""
|
||||||
if isinstance(other, str):
|
if isinstance(other, str):
|
||||||
other = Var.create(json.dumps(other))
|
other = Var.create(json.dumps(other))
|
||||||
@ -781,7 +784,7 @@ class Var:
|
|||||||
type_ = type_ or self._var_type
|
type_ = type_ or self._var_type
|
||||||
|
|
||||||
if other is None and flip:
|
if other is None and flip:
|
||||||
raise ValueError(
|
raise VarValueError(
|
||||||
"flip_operands cannot be set to True if the value of 'other' operand is not provided"
|
"flip_operands cannot be set to True if the value of 'other' operand is not provided"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -804,7 +807,7 @@ class Var:
|
|||||||
types.get_base_class(right_operand._var_type), # type: ignore
|
types.get_base_class(right_operand._var_type), # type: ignore
|
||||||
op,
|
op,
|
||||||
):
|
):
|
||||||
raise TypeError(
|
raise VarTypeError(
|
||||||
f"Unsupported Operand type(s) for {op}: `{left_operand._var_full_name}` of type {left_operand._var_type.__name__} and `{right_operand._var_full_name}` of type {right_operand._var_type.__name__}" # type: ignore
|
f"Unsupported Operand type(s) for {op}: `{left_operand._var_full_name}` of type {left_operand._var_type.__name__} and `{right_operand._var_full_name}` of type {right_operand._var_type.__name__}" # type: ignore
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -925,10 +928,10 @@ class Var:
|
|||||||
A var with the absolute value.
|
A var with the absolute value.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
TypeError: If the var is not a list.
|
VarTypeError: If the var is not a list.
|
||||||
"""
|
"""
|
||||||
if not types._issubclass(self._var_type, List):
|
if not types._issubclass(self._var_type, List):
|
||||||
raise TypeError(f"Cannot get length of non-list var {self}.")
|
raise VarTypeError(f"Cannot get length of non-list var {self}.")
|
||||||
return self._replace(
|
return self._replace(
|
||||||
_var_name=f"{self._var_name}.length",
|
_var_name=f"{self._var_name}.length",
|
||||||
_var_type=int,
|
_var_type=int,
|
||||||
@ -1328,9 +1331,9 @@ class Var:
|
|||||||
"""Override the 'in' operator to alert the user that it is not supported.
|
"""Override the 'in' operator to alert the user that it is not supported.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
TypeError: the operation is not supported
|
VarTypeError: the operation is not supported
|
||||||
"""
|
"""
|
||||||
raise TypeError(
|
raise VarTypeError(
|
||||||
"'in' operator not supported for Var types, use Var.contains() instead."
|
"'in' operator not supported for Var types, use Var.contains() instead."
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1341,13 +1344,13 @@ class Var:
|
|||||||
other: The object to check.
|
other: The object to check.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
TypeError: If the var is not a valid type: dict, list, tuple or str.
|
VarTypeError: If the var is not a valid type: dict, list, tuple or str.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
A var representing the contain check.
|
A var representing the contain check.
|
||||||
"""
|
"""
|
||||||
if not (types._issubclass(self._var_type, Union[dict, list, tuple, str, set])):
|
if not (types._issubclass(self._var_type, Union[dict, list, tuple, str, set])):
|
||||||
raise TypeError(
|
raise VarTypeError(
|
||||||
f"Var {self._var_full_name} of type {self._var_type} does not support contains check."
|
f"Var {self._var_full_name} of type {self._var_type} does not support contains check."
|
||||||
)
|
)
|
||||||
method = (
|
method = (
|
||||||
@ -1371,7 +1374,7 @@ class Var:
|
|||||||
if types._issubclass(self._var_type, str) and not types._issubclass(
|
if types._issubclass(self._var_type, str) and not types._issubclass(
|
||||||
other._var_type, str
|
other._var_type, str
|
||||||
):
|
):
|
||||||
raise TypeError(
|
raise VarTypeError(
|
||||||
f"'in <string>' requires string as left operand, not {other._var_type}"
|
f"'in <string>' requires string as left operand, not {other._var_type}"
|
||||||
)
|
)
|
||||||
return self._replace(
|
return self._replace(
|
||||||
@ -1385,13 +1388,13 @@ class Var:
|
|||||||
"""Reverse a list var.
|
"""Reverse a list var.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
TypeError: If the var is not a list.
|
VarTypeError: If the var is not a list.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
A var with the reversed list.
|
A var with the reversed list.
|
||||||
"""
|
"""
|
||||||
if not types._issubclass(self._var_type, list):
|
if not types._issubclass(self._var_type, list):
|
||||||
raise TypeError(f"Cannot reverse non-list var {self._var_full_name}.")
|
raise VarTypeError(f"Cannot reverse non-list var {self._var_full_name}.")
|
||||||
|
|
||||||
return self._replace(
|
return self._replace(
|
||||||
_var_name=f"[...{self._var_full_name}].reverse()",
|
_var_name=f"[...{self._var_full_name}].reverse()",
|
||||||
@ -1406,10 +1409,10 @@ class Var:
|
|||||||
A var with the lowercase string.
|
A var with the lowercase string.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
TypeError: If the var is not a string.
|
VarTypeError: If the var is not a string.
|
||||||
"""
|
"""
|
||||||
if not types._issubclass(self._var_type, str):
|
if not types._issubclass(self._var_type, str):
|
||||||
raise TypeError(
|
raise VarTypeError(
|
||||||
f"Cannot convert non-string var {self._var_full_name} to lowercase."
|
f"Cannot convert non-string var {self._var_full_name} to lowercase."
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1426,10 +1429,10 @@ class Var:
|
|||||||
A var with the uppercase string.
|
A var with the uppercase string.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
TypeError: If the var is not a string.
|
VarTypeError: If the var is not a string.
|
||||||
"""
|
"""
|
||||||
if not types._issubclass(self._var_type, str):
|
if not types._issubclass(self._var_type, str):
|
||||||
raise TypeError(
|
raise VarTypeError(
|
||||||
f"Cannot convert non-string var {self._var_full_name} to uppercase."
|
f"Cannot convert non-string var {self._var_full_name} to uppercase."
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1449,10 +1452,10 @@ class Var:
|
|||||||
A var with the stripped string.
|
A var with the stripped string.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
TypeError: If the var is not a string.
|
VarTypeError: If the var is not a string.
|
||||||
"""
|
"""
|
||||||
if not types._issubclass(self._var_type, str):
|
if not types._issubclass(self._var_type, str):
|
||||||
raise TypeError(f"Cannot strip non-string var {self._var_full_name}.")
|
raise VarTypeError(f"Cannot strip non-string var {self._var_full_name}.")
|
||||||
|
|
||||||
other = Var.create_safe(json.dumps(other)) if isinstance(other, str) else other
|
other = Var.create_safe(json.dumps(other)) if isinstance(other, str) else other
|
||||||
|
|
||||||
@ -1472,10 +1475,10 @@ class Var:
|
|||||||
A var with the list.
|
A var with the list.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
TypeError: If the var is not a string.
|
VarTypeError: If the var is not a string.
|
||||||
"""
|
"""
|
||||||
if not types._issubclass(self._var_type, str):
|
if not types._issubclass(self._var_type, str):
|
||||||
raise TypeError(f"Cannot split non-string var {self._var_full_name}.")
|
raise VarTypeError(f"Cannot split non-string var {self._var_full_name}.")
|
||||||
|
|
||||||
other = Var.create_safe(json.dumps(other)) if isinstance(other, str) else other
|
other = Var.create_safe(json.dumps(other)) if isinstance(other, str) else other
|
||||||
|
|
||||||
@ -1496,10 +1499,10 @@ class Var:
|
|||||||
A var with the string.
|
A var with the string.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
TypeError: If the var is not a list.
|
VarTypeError: If the var is not a list.
|
||||||
"""
|
"""
|
||||||
if not types._issubclass(self._var_type, list):
|
if not types._issubclass(self._var_type, list):
|
||||||
raise TypeError(f"Cannot join non-list var {self._var_full_name}.")
|
raise VarTypeError(f"Cannot join non-list var {self._var_full_name}.")
|
||||||
|
|
||||||
if other is None:
|
if other is None:
|
||||||
other = Var.create_safe('""')
|
other = Var.create_safe('""')
|
||||||
@ -1525,11 +1528,11 @@ class Var:
|
|||||||
A var representing foreach operation.
|
A var representing foreach operation.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
TypeError: If the var is not a list.
|
VarTypeError: If the var is not a list.
|
||||||
"""
|
"""
|
||||||
inner_types = get_args(self._var_type)
|
inner_types = get_args(self._var_type)
|
||||||
if not inner_types:
|
if not inner_types:
|
||||||
raise TypeError(
|
raise VarTypeError(
|
||||||
f"Cannot foreach over non-sequence var {self._var_full_name} of type {self._var_type}."
|
f"Cannot foreach over non-sequence var {self._var_full_name} of type {self._var_type}."
|
||||||
)
|
)
|
||||||
arg = BaseVar(
|
arg = BaseVar(
|
||||||
@ -1566,25 +1569,27 @@ class Var:
|
|||||||
A var representing range operation.
|
A var representing range operation.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
TypeError: If the var is not an int.
|
VarTypeError: If the var is not an int.
|
||||||
"""
|
"""
|
||||||
if not isinstance(v1, Var):
|
if not isinstance(v1, Var):
|
||||||
v1 = Var.create_safe(v1)
|
v1 = Var.create_safe(v1)
|
||||||
if v1._var_type != int:
|
if v1._var_type != int:
|
||||||
raise TypeError(f"Cannot get range on non-int var {v1._var_full_name}.")
|
raise VarTypeError(f"Cannot get range on non-int var {v1._var_full_name}.")
|
||||||
if not isinstance(v2, Var):
|
if not isinstance(v2, Var):
|
||||||
v2 = Var.create(v2)
|
v2 = Var.create(v2)
|
||||||
if v2 is None:
|
if v2 is None:
|
||||||
v2 = Var.create_safe("undefined")
|
v2 = Var.create_safe("undefined")
|
||||||
elif v2._var_type != int:
|
elif v2._var_type != int:
|
||||||
raise TypeError(f"Cannot get range on non-int var {v2._var_full_name}.")
|
raise VarTypeError(f"Cannot get range on non-int var {v2._var_full_name}.")
|
||||||
|
|
||||||
if not isinstance(step, Var):
|
if not isinstance(step, Var):
|
||||||
step = Var.create(step)
|
step = Var.create(step)
|
||||||
if step is None:
|
if step is None:
|
||||||
step = Var.create_safe(1)
|
step = Var.create_safe(1)
|
||||||
elif step._var_type != int:
|
elif step._var_type != int:
|
||||||
raise TypeError(f"Cannot get range on non-int var {step._var_full_name}.")
|
raise VarTypeError(
|
||||||
|
f"Cannot get range on non-int var {step._var_full_name}."
|
||||||
|
)
|
||||||
|
|
||||||
return BaseVar(
|
return BaseVar(
|
||||||
_var_name=f"Array.from(range({v1._var_full_name}, {v2._var_full_name}, {step._var_name}))",
|
_var_name=f"Array.from(range({v1._var_full_name}, {v2._var_full_name}, {step._var_name}))",
|
||||||
@ -1919,7 +1924,7 @@ class ComputedVar(Var, property):
|
|||||||
A set of variable names accessed by the given obj.
|
A set of variable names accessed by the given obj.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
ValueError: if the function references the get_state, parent_state, or substates attributes
|
VarValueError: if the function references the get_state, parent_state, or substates attributes
|
||||||
(cannot track deps in a related state, only implicitly via parent state).
|
(cannot track deps in a related state, only implicitly via parent state).
|
||||||
"""
|
"""
|
||||||
d = set()
|
d = set()
|
||||||
@ -1966,7 +1971,7 @@ class ComputedVar(Var, property):
|
|||||||
except Exception:
|
except Exception:
|
||||||
ref_obj = None
|
ref_obj = None
|
||||||
if instruction.argval in invalid_names:
|
if instruction.argval in invalid_names:
|
||||||
raise ValueError(
|
raise VarValueError(
|
||||||
f"Cached var {self._var_full_name} cannot access arbitrary state via `{instruction.argval}`."
|
f"Cached var {self._var_full_name} cannot access arbitrary state via `{instruction.argval}`."
|
||||||
)
|
)
|
||||||
if callable(ref_obj):
|
if callable(ref_obj):
|
||||||
|
Loading…
Reference in New Issue
Block a user