[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.
|
||||
|
||||
Raises:
|
||||
VarOperationTypeError: When an invalid component var related 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.
|
||||
"""
|
||||
from reflex.utils.exceptions import VarOperationTypeError
|
||||
|
||||
try:
|
||||
return component if isinstance(component, Component) else component()
|
||||
except exceptions.MatchTypeError:
|
||||
@ -413,7 +416,7 @@ class App(Base):
|
||||
except TypeError as e:
|
||||
message = str(e)
|
||||
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. "
|
||||
"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/"
|
||||
@ -555,11 +558,13 @@ class App(Base):
|
||||
Based on conflicts that NextJS would throw if not intercepted.
|
||||
|
||||
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:
|
||||
new_route: the route being newly added.
|
||||
"""
|
||||
from reflex.utils.exceptions import RouteValueError
|
||||
|
||||
if "[" not in new_route:
|
||||
return
|
||||
|
||||
@ -576,7 +581,7 @@ class App(Base):
|
||||
):
|
||||
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
|
||||
raise ValueError(
|
||||
raise RouteValueError(
|
||||
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:
|
||||
@ -755,8 +760,10 @@ class App(Base):
|
||||
export: Whether to compile the app for export.
|
||||
|
||||
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
|
||||
if constants.Page404.SLUG not in self.pages:
|
||||
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.
|
||||
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 "
|
||||
"subclass of rx.State must be defined in the app."
|
||||
)
|
||||
@ -1137,10 +1144,12 @@ def upload(app: App):
|
||||
emitted by the upload handler.
|
||||
|
||||
Raises:
|
||||
ValueError: if there are no args with supported annotation.
|
||||
TypeError: if a background task is used as the handler.
|
||||
UploadValueError: if there are no args with supported annotation.
|
||||
UploadTypeError: if a background task is used as the handler.
|
||||
HTTPException: when the request does not include token / handler headers.
|
||||
"""
|
||||
from reflex.utils.exceptions import UploadTypeError, UploadValueError
|
||||
|
||||
token = request.headers.get("reflex-client-token")
|
||||
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]
|
||||
if isinstance(func, EventHandler):
|
||||
if func.is_background:
|
||||
raise TypeError(
|
||||
raise UploadTypeError(
|
||||
f"@rx.background is not supported for upload handler `{handler}`.",
|
||||
)
|
||||
func = func.fn
|
||||
@ -1181,7 +1190,7 @@ def upload(app: App):
|
||||
break
|
||||
|
||||
if not handler_upload_param:
|
||||
raise ValueError(
|
||||
raise UploadValueError(
|
||||
f"`{handler}` handler should have a parameter annotated as "
|
||||
"List[rx.UploadFile]"
|
||||
)
|
||||
|
@ -1,4 +1,5 @@
|
||||
"""Define the base Reflex class."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
@ -26,15 +27,17 @@ def validate_field_name(bases: List[Type["BaseModel"]], field_name: str) -> None
|
||||
field_name: name of attribute
|
||||
|
||||
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"
|
||||
for base in bases:
|
||||
try:
|
||||
if not reload and getattr(base, field_name, None):
|
||||
pass
|
||||
except TypeError as te:
|
||||
raise NameError(
|
||||
raise VarNameError(
|
||||
f'State var "{field_name}" in {base} has been shadowed by a substate var; '
|
||||
f'use a different field name instead".'
|
||||
) from te
|
||||
|
@ -731,6 +731,7 @@ class Component(BaseComponent, ABC):
|
||||
# Import here to avoid circular imports.
|
||||
from reflex.components.base.bare import Bare
|
||||
from reflex.components.base.fragment import Fragment
|
||||
from reflex.utils.exceptions import ComponentTypeError
|
||||
|
||||
# Translate deprecated props to new names.
|
||||
new_prop_names = [
|
||||
@ -757,7 +758,7 @@ class Component(BaseComponent, ABC):
|
||||
validate_children(child)
|
||||
# Make sure the child is a valid type.
|
||||
if not types._isinstance(child, ComponentChild):
|
||||
raise TypeError(
|
||||
raise ComponentTypeError(
|
||||
"Children of Reflex components must be other components, "
|
||||
"state vars, or primitive Python types. "
|
||||
f"Got child {child} of type {type(child)}.",
|
||||
|
@ -251,8 +251,10 @@ class Config(Base):
|
||||
The updated config values.
|
||||
|
||||
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 = {}
|
||||
# Iterate over the fields.
|
||||
for key, field in self.__fields__.items():
|
||||
@ -273,11 +275,11 @@ class Config(Base):
|
||||
env_var = env_var.lower() in ["true", "1", "yes"]
|
||||
else:
|
||||
env_var = field.type_(env_var)
|
||||
except ValueError:
|
||||
except ValueError as ve:
|
||||
console.error(
|
||||
f"Could not convert {key.upper()}={env_var} to type {field.type_}"
|
||||
)
|
||||
raise
|
||||
raise EnvVarValueError from ve
|
||||
|
||||
# Set the value.
|
||||
updated_values[key] = env_var
|
||||
|
@ -180,8 +180,10 @@ class EventHandler(EventActionsMixin):
|
||||
The event spec, containing both the function and args.
|
||||
|
||||
Raises:
|
||||
TypeError: If the arguments are invalid.
|
||||
EventHandlerTypeError: If the arguments are invalid.
|
||||
"""
|
||||
from reflex.utils.exceptions import EventHandlerTypeError
|
||||
|
||||
# Get the function args.
|
||||
fn_args = inspect.getfullargspec(self.fn).args[1:]
|
||||
fn_args = (Var.create_safe(arg) for arg in fn_args)
|
||||
@ -197,7 +199,7 @@ class EventHandler(EventActionsMixin):
|
||||
try:
|
||||
values.append(Var.create(arg, _var_is_string=isinstance(arg, str)))
|
||||
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)}."
|
||||
) from e
|
||||
payload = tuple(zip(fn_args, values))
|
||||
@ -256,8 +258,10 @@ class EventSpec(EventActionsMixin):
|
||||
The event spec with the new arguments.
|
||||
|
||||
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.
|
||||
fn_args = inspect.getfullargspec(self.handler.fn).args[1 + len(self.args) :]
|
||||
fn_args = (Var.create_safe(arg) for arg in fn_args)
|
||||
@ -268,7 +272,7 @@ class EventSpec(EventActionsMixin):
|
||||
try:
|
||||
values.append(Var.create(arg, _var_is_string=isinstance(arg, str)))
|
||||
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)}."
|
||||
) from e
|
||||
new_payload = tuple(zip(fn_args, values))
|
||||
@ -312,10 +316,12 @@ class CallableEventSpec(EventSpec):
|
||||
The EventSpec returned from calling the function.
|
||||
|
||||
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:
|
||||
raise TypeError("CallableEventSpec has no associated function.")
|
||||
raise EventHandlerTypeError("CallableEventSpec has no associated function.")
|
||||
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.
|
||||
|
||||
Raises:
|
||||
ValueError: If the lambda has an invalid signature.
|
||||
EventHandlerValueError: If the lambda has an invalid signature.
|
||||
"""
|
||||
# Import here to avoid circular imports.
|
||||
from reflex.event import EventHandler, EventSpec
|
||||
from reflex.utils.exceptions import EventHandlerValueError
|
||||
|
||||
# Get the args of the lambda.
|
||||
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:
|
||||
out = fn(arg)
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
events.append(e)
|
||||
|
@ -279,11 +279,13 @@ class EventHandlerSetVar(EventHandler):
|
||||
|
||||
Raises:
|
||||
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 not isinstance(args[0], str):
|
||||
raise ValueError(
|
||||
raise EventHandlerValueError(
|
||||
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.
|
||||
@ -380,10 +382,12 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
||||
**kwargs: The kwargs to pass to the Pydantic init method.
|
||||
|
||||
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():
|
||||
raise RuntimeError(
|
||||
raise ReflexRuntimeError(
|
||||
"State classes should not be instantiated directly in a Reflex app. "
|
||||
"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.
|
||||
|
||||
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)
|
||||
# Event handlers should not shadow builtin state methods.
|
||||
cls._check_overridden_methods()
|
||||
@ -471,7 +477,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
||||
else:
|
||||
# During normal operation, subclasses cannot have the same name, even if they are
|
||||
# defined in different modules.
|
||||
raise ValueError(
|
||||
raise StateValueError(
|
||||
f"The substate class '{cls.__name__}' has been defined multiple times. "
|
||||
"Shadowing substate classes is not allowed."
|
||||
)
|
||||
@ -829,10 +835,12 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
||||
prop: The variable to initialize
|
||||
|
||||
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):
|
||||
raise TypeError(
|
||||
raise VarTypeError(
|
||||
"State vars must be primitive Python types, "
|
||||
"Plotly figures, Pandas dataframes, "
|
||||
"or subclasses of rx.Base. "
|
||||
@ -1688,10 +1696,12 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
||||
if initial:
|
||||
computed_vars = {
|
||||
# Include initial computed vars.
|
||||
prop_name: cv._initial_value
|
||||
if isinstance(cv, ComputedVar)
|
||||
and not isinstance(cv._initial_value, types.Unset)
|
||||
else self.get_value(getattr(self, prop_name))
|
||||
prop_name: (
|
||||
cv._initial_value
|
||||
if isinstance(cv, ComputedVar)
|
||||
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()
|
||||
}
|
||||
elif include_computed:
|
||||
|
@ -1,21 +1,77 @@
|
||||
"""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."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class ImmutableStateError(AttributeError):
|
||||
class ImmutableStateError(ReflexError):
|
||||
"""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."""
|
||||
|
||||
|
||||
class MatchTypeError(TypeError):
|
||||
class MatchTypeError(ReflexError, TypeError):
|
||||
"""Raised when the return types of match cases are different."""
|
||||
|
||||
pass
|
||||
|
@ -210,28 +210,35 @@ def get_app(reload: bool = False) -> ModuleType:
|
||||
|
||||
Raises:
|
||||
RuntimeError: If the app name is not set in the config.
|
||||
exceptions.ReflexError: Reflex specific errors.
|
||||
"""
|
||||
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,))
|
||||
from reflex.utils import exceptions, telemetry
|
||||
|
||||
if reload:
|
||||
from reflex.state import reload_state_module
|
||||
try:
|
||||
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.
|
||||
reload_state_module(module=module)
|
||||
if reload:
|
||||
from reflex.state import reload_state_module
|
||||
|
||||
# Reload the app module.
|
||||
importlib.reload(app)
|
||||
# Reset rx.State subclasses to avoid conflict when reloading.
|
||||
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:
|
||||
|
107
reflex/vars.py
107
reflex/vars.py
@ -35,6 +35,7 @@ from typing import (
|
||||
from reflex import constants
|
||||
from reflex.base import Base
|
||||
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
|
||||
from reflex.utils.imports import ImportDict, ImportVar
|
||||
@ -353,7 +354,7 @@ class Var:
|
||||
The var.
|
||||
|
||||
Raises:
|
||||
TypeError: If the value is JSON-unserializable.
|
||||
VarTypeError: If the value is JSON-unserializable.
|
||||
"""
|
||||
# Check for none values.
|
||||
if value is None:
|
||||
@ -372,7 +373,7 @@ class Var:
|
||||
type_ = type(value)
|
||||
name = value if type_ in types.JSONType else serializers.serialize(value)
|
||||
if name is None:
|
||||
raise TypeError(
|
||||
raise VarTypeError(
|
||||
f"No JSON serializer found for var {value} of type {type_}."
|
||||
)
|
||||
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.
|
||||
|
||||
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`. "
|
||||
"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.
|
||||
|
||||
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`."
|
||||
)
|
||||
|
||||
@ -584,7 +585,7 @@ class Var:
|
||||
The indexed var.
|
||||
|
||||
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.
|
||||
if not (
|
||||
@ -592,11 +593,11 @@ class Var:
|
||||
or types.is_dataframe(self._var_type)
|
||||
):
|
||||
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, "
|
||||
"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."
|
||||
)
|
||||
|
||||
@ -615,7 +616,7 @@ class Var:
|
||||
or isinstance(i, Var)
|
||||
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.
|
||||
if isinstance(i, slice):
|
||||
@ -658,7 +659,7 @@ class Var:
|
||||
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"
|
||||
)
|
||||
# Get the type of the indexed var.
|
||||
@ -687,7 +688,7 @@ class Var:
|
||||
The var attribute.
|
||||
|
||||
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:
|
||||
var_attribute = super().__getattribute__(name)
|
||||
@ -698,10 +699,12 @@ class Var:
|
||||
super().__getattribute__("_var_type"), name
|
||||
)
|
||||
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 var_attribute
|
||||
except AttributeError:
|
||||
except VarAttributeError:
|
||||
raise # fall back to __getattr__ anyway
|
||||
|
||||
def __getattr__(self, name: str) -> Var:
|
||||
@ -714,13 +717,13 @@ class Var:
|
||||
The var attribute.
|
||||
|
||||
Raises:
|
||||
AttributeError: If the var is wrongly annotated or can't find attribute.
|
||||
TypeError: If an annotation to the var isn't provided.
|
||||
VarAttributeError: If the var is wrongly annotated or can't find attribute.
|
||||
VarTypeError: If an annotation to the var isn't provided.
|
||||
"""
|
||||
# Check if the attribute is one of the class fields.
|
||||
if not name.startswith("_"):
|
||||
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}`"
|
||||
) from None
|
||||
is_optional = types.is_optional(self._var_type)
|
||||
@ -734,16 +737,16 @@ class Var:
|
||||
)
|
||||
|
||||
if name in REPLACED_NAMES:
|
||||
raise AttributeError(
|
||||
raise VarAttributeError(
|
||||
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"wrongly."
|
||||
)
|
||||
|
||||
raise AttributeError(
|
||||
raise VarAttributeError(
|
||||
f"The State var has no attribute '{name}' or may have been annotated wrongly.",
|
||||
)
|
||||
|
||||
@ -770,8 +773,8 @@ class Var:
|
||||
The operation result.
|
||||
|
||||
Raises:
|
||||
TypeError: If the operation between two operands is invalid.
|
||||
ValueError: If flip is set to true and value of operand is not provided
|
||||
VarTypeError: If the operation between two operands is invalid.
|
||||
VarValueError: If flip is set to true and value of operand is not provided
|
||||
"""
|
||||
if isinstance(other, str):
|
||||
other = Var.create(json.dumps(other))
|
||||
@ -781,7 +784,7 @@ class Var:
|
||||
type_ = type_ or self._var_type
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
@ -804,7 +807,7 @@ class Var:
|
||||
types.get_base_class(right_operand._var_type), # type: ignore
|
||||
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
|
||||
)
|
||||
|
||||
@ -925,10 +928,10 @@ class Var:
|
||||
A var with the absolute value.
|
||||
|
||||
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):
|
||||
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(
|
||||
_var_name=f"{self._var_name}.length",
|
||||
_var_type=int,
|
||||
@ -1328,9 +1331,9 @@ class Var:
|
||||
"""Override the 'in' operator to alert the user that it is not supported.
|
||||
|
||||
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."
|
||||
)
|
||||
|
||||
@ -1341,13 +1344,13 @@ class Var:
|
||||
other: The object to check.
|
||||
|
||||
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:
|
||||
A var representing the contain check.
|
||||
"""
|
||||
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."
|
||||
)
|
||||
method = (
|
||||
@ -1371,7 +1374,7 @@ class Var:
|
||||
if types._issubclass(self._var_type, str) and not types._issubclass(
|
||||
other._var_type, str
|
||||
):
|
||||
raise TypeError(
|
||||
raise VarTypeError(
|
||||
f"'in <string>' requires string as left operand, not {other._var_type}"
|
||||
)
|
||||
return self._replace(
|
||||
@ -1385,13 +1388,13 @@ class Var:
|
||||
"""Reverse a list var.
|
||||
|
||||
Raises:
|
||||
TypeError: If the var is not a list.
|
||||
VarTypeError: If the var is not a list.
|
||||
|
||||
Returns:
|
||||
A var with the reversed 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(
|
||||
_var_name=f"[...{self._var_full_name}].reverse()",
|
||||
@ -1406,10 +1409,10 @@ class Var:
|
||||
A var with the lowercase string.
|
||||
|
||||
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):
|
||||
raise TypeError(
|
||||
raise VarTypeError(
|
||||
f"Cannot convert non-string var {self._var_full_name} to lowercase."
|
||||
)
|
||||
|
||||
@ -1426,10 +1429,10 @@ class Var:
|
||||
A var with the uppercase string.
|
||||
|
||||
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):
|
||||
raise TypeError(
|
||||
raise VarTypeError(
|
||||
f"Cannot convert non-string var {self._var_full_name} to uppercase."
|
||||
)
|
||||
|
||||
@ -1449,10 +1452,10 @@ class Var:
|
||||
A var with the stripped string.
|
||||
|
||||
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):
|
||||
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
|
||||
|
||||
@ -1472,10 +1475,10 @@ class Var:
|
||||
A var with the list.
|
||||
|
||||
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):
|
||||
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
|
||||
|
||||
@ -1496,10 +1499,10 @@ class Var:
|
||||
A var with the string.
|
||||
|
||||
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):
|
||||
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:
|
||||
other = Var.create_safe('""')
|
||||
@ -1525,11 +1528,11 @@ class Var:
|
||||
A var representing foreach operation.
|
||||
|
||||
Raises:
|
||||
TypeError: If the var is not a list.
|
||||
VarTypeError: If the var is not a list.
|
||||
"""
|
||||
inner_types = get_args(self._var_type)
|
||||
if not inner_types:
|
||||
raise TypeError(
|
||||
raise VarTypeError(
|
||||
f"Cannot foreach over non-sequence var {self._var_full_name} of type {self._var_type}."
|
||||
)
|
||||
arg = BaseVar(
|
||||
@ -1566,25 +1569,27 @@ class Var:
|
||||
A var representing range operation.
|
||||
|
||||
Raises:
|
||||
TypeError: If the var is not an int.
|
||||
VarTypeError: If the var is not an int.
|
||||
"""
|
||||
if not isinstance(v1, Var):
|
||||
v1 = Var.create_safe(v1)
|
||||
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):
|
||||
v2 = Var.create(v2)
|
||||
if v2 is None:
|
||||
v2 = Var.create_safe("undefined")
|
||||
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):
|
||||
step = Var.create(step)
|
||||
if step is None:
|
||||
step = Var.create_safe(1)
|
||||
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(
|
||||
_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.
|
||||
|
||||
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).
|
||||
"""
|
||||
d = set()
|
||||
@ -1966,7 +1971,7 @@ class ComputedVar(Var, property):
|
||||
except Exception:
|
||||
ref_obj = None
|
||||
if instruction.argval in invalid_names:
|
||||
raise ValueError(
|
||||
raise VarValueError(
|
||||
f"Cached var {self._var_full_name} cannot access arbitrary state via `{instruction.argval}`."
|
||||
)
|
||||
if callable(ref_obj):
|
||||
|
Loading…
Reference in New Issue
Block a user