123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804 |
- """Classes for immutable object vars."""
- from __future__ import annotations
- import dataclasses
- import sys
- import typing
- from functools import cached_property
- from inspect import isclass
- from typing import (
- Any,
- Dict,
- List,
- NoReturn,
- Tuple,
- Type,
- TypeVar,
- Union,
- get_args,
- overload,
- )
- from typing_extensions import get_origin
- from reflex.experimental.vars.base import (
- ImmutableVar,
- LiteralVar,
- figure_out_type,
- )
- from reflex.experimental.vars.number import NumberVar
- from reflex.experimental.vars.sequence import ArrayVar, StringVar
- from reflex.utils.exceptions import VarAttributeError
- from reflex.utils.types import GenericType, get_attribute_access_type
- from reflex.vars import ImmutableVarData, Var, VarData
- OBJECT_TYPE = TypeVar("OBJECT_TYPE")
- KEY_TYPE = TypeVar("KEY_TYPE")
- VALUE_TYPE = TypeVar("VALUE_TYPE")
- ARRAY_INNER_TYPE = TypeVar("ARRAY_INNER_TYPE")
- OTHER_KEY_TYPE = TypeVar("OTHER_KEY_TYPE")
- class ObjectVar(ImmutableVar[OBJECT_TYPE]):
- """Base class for immutable object vars."""
- @overload
- def _key_type(self: ObjectVar[Dict[KEY_TYPE, VALUE_TYPE]]) -> KEY_TYPE: ...
- @overload
- def _key_type(self) -> Type: ...
- def _key_type(self) -> Type:
- """Get the type of the keys of the object.
- Returns:
- The type of the keys of the object.
- """
- fixed_type = (
- self._var_type if isclass(self._var_type) else get_origin(self._var_type)
- )
- args = get_args(self._var_type) if issubclass(fixed_type, dict) else ()
- return args[0] if args else Any
- @overload
- def _value_type(self: ObjectVar[Dict[KEY_TYPE, VALUE_TYPE]]) -> VALUE_TYPE: ...
- @overload
- def _value_type(self) -> Type: ...
- def _value_type(self) -> Type:
- """Get the type of the values of the object.
- Returns:
- The type of the values of the object.
- """
- fixed_type = (
- self._var_type if isclass(self._var_type) else get_origin(self._var_type)
- )
- args = get_args(self._var_type) if issubclass(fixed_type, dict) else ()
- return args[1] if args else Any
- @overload
- def keys(
- self: ObjectVar[Dict[KEY_TYPE, VALUE_TYPE]],
- ) -> ArrayVar[List[KEY_TYPE]]: ...
- @overload
- def keys(self) -> ArrayVar: ...
- def keys(self) -> ArrayVar:
- """Get the keys of the object.
- Returns:
- The keys of the object.
- """
- return ObjectKeysOperation(self)
- @overload
- def values(
- self: ObjectVar[Dict[KEY_TYPE, VALUE_TYPE]],
- ) -> ArrayVar[List[VALUE_TYPE]]: ...
- @overload
- def values(self) -> ArrayVar: ...
- def values(self) -> ArrayVar:
- """Get the values of the object.
- Returns:
- The values of the object.
- """
- return ObjectValuesOperation(self)
- @overload
- def entries(
- self: ObjectVar[Dict[KEY_TYPE, VALUE_TYPE]],
- ) -> ArrayVar[List[Tuple[KEY_TYPE, VALUE_TYPE]]]: ...
- @overload
- def entries(self) -> ArrayVar: ...
- def entries(self) -> ArrayVar:
- """Get the entries of the object.
- Returns:
- The entries of the object.
- """
- return ObjectEntriesOperation(self)
- def merge(self, other: ObjectVar) -> ObjectMergeOperation:
- """Merge two objects.
- Args:
- other: The other object to merge.
- Returns:
- The merged object.
- """
- return ObjectMergeOperation(self, other)
- # NoReturn is used here to catch when key value is Any
- @overload
- def __getitem__(
- self: ObjectVar[Dict[KEY_TYPE, NoReturn]],
- key: Var | Any,
- ) -> ImmutableVar: ...
- @overload
- def __getitem__(
- self: (
- ObjectVar[Dict[KEY_TYPE, int]]
- | ObjectVar[Dict[KEY_TYPE, float]]
- | ObjectVar[Dict[KEY_TYPE, int | float]]
- ),
- key: Var | Any,
- ) -> NumberVar: ...
- @overload
- def __getitem__(
- self: ObjectVar[Dict[KEY_TYPE, str]],
- key: Var | Any,
- ) -> StringVar: ...
- @overload
- def __getitem__(
- self: ObjectVar[Dict[KEY_TYPE, list[ARRAY_INNER_TYPE]]],
- key: Var | Any,
- ) -> ArrayVar[list[ARRAY_INNER_TYPE]]: ...
- @overload
- def __getitem__(
- self: ObjectVar[Dict[KEY_TYPE, set[ARRAY_INNER_TYPE]]],
- key: Var | Any,
- ) -> ArrayVar[set[ARRAY_INNER_TYPE]]: ...
- @overload
- def __getitem__(
- self: ObjectVar[Dict[KEY_TYPE, tuple[ARRAY_INNER_TYPE, ...]]],
- key: Var | Any,
- ) -> ArrayVar[tuple[ARRAY_INNER_TYPE, ...]]: ...
- @overload
- def __getitem__(
- self: ObjectVar[Dict[KEY_TYPE, dict[OTHER_KEY_TYPE, VALUE_TYPE]]],
- key: Var | Any,
- ) -> ObjectVar[dict[OTHER_KEY_TYPE, VALUE_TYPE]]: ...
- def __getitem__(self, key: Var | Any) -> ImmutableVar:
- """Get an item from the object.
- Args:
- key: The key to get from the object.
- Returns:
- The item from the object.
- """
- return ObjectItemOperation(self, key).guess_type()
- # NoReturn is used here to catch when key value is Any
- @overload
- def __getattr__(
- self: ObjectVar[Dict[KEY_TYPE, NoReturn]],
- name: str,
- ) -> ImmutableVar: ...
- @overload
- def __getattr__(
- self: (
- ObjectVar[Dict[KEY_TYPE, int]]
- | ObjectVar[Dict[KEY_TYPE, float]]
- | ObjectVar[Dict[KEY_TYPE, int | float]]
- ),
- name: str,
- ) -> NumberVar: ...
- @overload
- def __getattr__(
- self: ObjectVar[Dict[KEY_TYPE, str]],
- name: str,
- ) -> StringVar: ...
- @overload
- def __getattr__(
- self: ObjectVar[Dict[KEY_TYPE, list[ARRAY_INNER_TYPE]]],
- name: str,
- ) -> ArrayVar[list[ARRAY_INNER_TYPE]]: ...
- @overload
- def __getattr__(
- self: ObjectVar[Dict[KEY_TYPE, set[ARRAY_INNER_TYPE]]],
- name: str,
- ) -> ArrayVar[set[ARRAY_INNER_TYPE]]: ...
- @overload
- def __getattr__(
- self: ObjectVar[Dict[KEY_TYPE, tuple[ARRAY_INNER_TYPE, ...]]],
- name: str,
- ) -> ArrayVar[tuple[ARRAY_INNER_TYPE, ...]]: ...
- @overload
- def __getattr__(
- self: ObjectVar[Dict[KEY_TYPE, dict[OTHER_KEY_TYPE, VALUE_TYPE]]],
- name: str,
- ) -> ObjectVar[dict[OTHER_KEY_TYPE, VALUE_TYPE]]: ...
- def __getattr__(self, name) -> ImmutableVar:
- """Get an attribute of the var.
- Args:
- name: The name of the attribute.
- Raises:
- VarAttributeError: The State var has no such attribute or may have been annotated wrongly.
- Returns:
- The attribute of the var.
- """
- fixed_type = (
- self._var_type if isclass(self._var_type) else get_origin(self._var_type)
- )
- if not issubclass(fixed_type, dict):
- attribute_type = get_attribute_access_type(self._var_type, name)
- if attribute_type is None:
- raise VarAttributeError(
- f"The State var `{self._var_name}` has no attribute '{name}' or may have been annotated "
- f"wrongly."
- )
- return ObjectItemOperation(self, name, attribute_type).guess_type()
- else:
- return ObjectItemOperation(self, name).guess_type()
- @dataclasses.dataclass(
- eq=False,
- frozen=True,
- **{"slots": True} if sys.version_info >= (3, 10) else {},
- )
- class LiteralObjectVar(LiteralVar, ObjectVar[OBJECT_TYPE]):
- """Base class for immutable literal object vars."""
- _var_value: Dict[Union[Var, Any], Union[Var, Any]] = dataclasses.field(
- default_factory=dict
- )
- def __init__(
- self: LiteralObjectVar[OBJECT_TYPE],
- _var_value: OBJECT_TYPE,
- _var_type: Type[OBJECT_TYPE] | None = None,
- _var_data: VarData | None = None,
- ):
- """Initialize the object var.
- Args:
- _var_value: The value of the var.
- _var_type: The type of the var.
- _var_data: Additional hooks and imports associated with the Var.
- """
- super(LiteralObjectVar, self).__init__(
- _var_name="",
- _var_type=(figure_out_type(_var_value) if _var_type is None else _var_type),
- _var_data=ImmutableVarData.merge(_var_data),
- )
- object.__setattr__(
- self,
- "_var_value",
- _var_value,
- )
- object.__delattr__(self, "_var_name")
- def _key_type(self) -> Type:
- """Get the type of the keys of the object.
- Returns:
- The type of the keys of the object.
- """
- args_list = typing.get_args(self._var_type)
- return args_list[0] if args_list else Any
- def _value_type(self) -> Type:
- """Get the type of the values of the object.
- Returns:
- The type of the values of the object.
- """
- args_list = typing.get_args(self._var_type)
- return args_list[1] if args_list else Any
- def __getattr__(self, name):
- """Get an attribute of the var.
- Args:
- name: The name of the attribute.
- Returns:
- The attribute of the var.
- """
- if name == "_var_name":
- return self._cached_var_name
- return super(type(self), self).__getattr__(name)
- @cached_property
- def _cached_var_name(self) -> str:
- """The name of the var.
- Returns:
- The name of the var.
- """
- return (
- "({ "
- + ", ".join(
- [
- f"[{str(LiteralVar.create(key))}] : {str(LiteralVar.create(value))}"
- for key, value in self._var_value.items()
- ]
- )
- + " })"
- )
- @cached_property
- def _cached_get_all_var_data(self) -> ImmutableVarData | None:
- """Get all VarData associated with the Var.
- Returns:
- The VarData of the components and all of its children.
- """
- return ImmutableVarData.merge(
- *[
- value._get_all_var_data()
- for key, value in self._var_value
- if isinstance(value, Var)
- ],
- *[
- key._get_all_var_data()
- for key, value in self._var_value
- if isinstance(key, Var)
- ],
- self._var_data,
- )
- def _get_all_var_data(self) -> ImmutableVarData | None:
- """Wrapper method for cached property.
- Returns:
- The VarData of the components and all of its children.
- """
- return self._cached_get_all_var_data
- def json(self) -> str:
- """Get the JSON representation of the object.
- Returns:
- The JSON representation of the object.
- """
- return (
- "{"
- + ", ".join(
- [
- f"{LiteralVar.create(key).json()}:{LiteralVar.create(value).json()}"
- for key, value in self._var_value.items()
- ]
- )
- + "}"
- )
- def __hash__(self) -> int:
- """Get the hash of the var.
- Returns:
- The hash of the var.
- """
- return hash((self.__class__.__name__, self._var_name))
- @dataclasses.dataclass(
- eq=False,
- frozen=True,
- **{"slots": True} if sys.version_info >= (3, 10) else {},
- )
- class ObjectToArrayOperation(ArrayVar):
- """Base class for object to array operations."""
- value: ObjectVar = dataclasses.field(default_factory=lambda: LiteralObjectVar({}))
- def __init__(
- self,
- _var_value: ObjectVar,
- _var_type: Type = list,
- _var_data: VarData | None = None,
- ):
- """Initialize the object to array operation.
- Args:
- _var_value: The value of the operation.
- _var_data: Additional hooks and imports associated with the operation.
- """
- super(ObjectToArrayOperation, self).__init__(
- _var_name="",
- _var_type=_var_type,
- _var_data=ImmutableVarData.merge(_var_data),
- )
- object.__setattr__(self, "value", _var_value)
- object.__delattr__(self, "_var_name")
- @cached_property
- def _cached_var_name(self) -> str:
- """The name of the operation.
- Raises:
- NotImplementedError: Must implement _cached_var_name.
- """
- raise NotImplementedError(
- "ObjectToArrayOperation must implement _cached_var_name"
- )
- def __getattr__(self, name):
- """Get an attribute of the operation.
- Args:
- name: The name of the attribute.
- Returns:
- The attribute of the operation.
- """
- if name == "_var_name":
- return self._cached_var_name
- return super(type(self), self).__getattr__(name)
- @cached_property
- def _cached_get_all_var_data(self) -> ImmutableVarData | None:
- """Get all VarData associated with the operation.
- Returns:
- The VarData of the components and all of its children.
- """
- return ImmutableVarData.merge(
- self.value._get_all_var_data(),
- self._var_data,
- )
- def _get_all_var_data(self) -> ImmutableVarData | None:
- """Wrapper method for cached property.
- Returns:
- The VarData of the components and all of its children.
- """
- return self._cached_get_all_var_data
- class ObjectKeysOperation(ObjectToArrayOperation):
- """Operation to get the keys of an object."""
- def __init__(
- self,
- value: ObjectVar,
- _var_data: VarData | None = None,
- ):
- """Initialize the object keys operation.
- Args:
- value: The value of the operation.
- _var_data: Additional hooks and imports associated with the operation.
- """
- super(ObjectKeysOperation, self).__init__(
- value, List[value._key_type()], _var_data
- )
- @cached_property
- def _cached_var_name(self) -> str:
- """The name of the operation.
- Returns:
- The name of the operation.
- """
- return f"Object.keys({self.value._var_name})"
- class ObjectValuesOperation(ObjectToArrayOperation):
- """Operation to get the values of an object."""
- def __init__(
- self,
- value: ObjectVar,
- _var_data: VarData | None = None,
- ):
- """Initialize the object values operation.
- Args:
- value: The value of the operation.
- _var_data: Additional hooks and imports associated with the operation.
- """
- super(ObjectValuesOperation, self).__init__(
- value, List[value._value_type()], _var_data
- )
- @cached_property
- def _cached_var_name(self) -> str:
- """The name of the operation.
- Returns:
- The name of the operation.
- """
- return f"Object.values({self.value._var_name})"
- class ObjectEntriesOperation(ObjectToArrayOperation):
- """Operation to get the entries of an object."""
- def __init__(
- self,
- value: ObjectVar,
- _var_data: VarData | None = None,
- ):
- """Initialize the object entries operation.
- Args:
- value: The value of the operation.
- _var_data: Additional hooks and imports associated with the operation.
- """
- super(ObjectEntriesOperation, self).__init__(
- value, List[Tuple[value._key_type(), value._value_type()]], _var_data
- )
- @cached_property
- def _cached_var_name(self) -> str:
- """The name of the operation.
- Returns:
- The name of the operation.
- """
- return f"Object.entries({self.value._var_name})"
- @dataclasses.dataclass(
- eq=False,
- frozen=True,
- **{"slots": True} if sys.version_info >= (3, 10) else {},
- )
- class ObjectMergeOperation(ObjectVar):
- """Operation to merge two objects."""
- left: ObjectVar = dataclasses.field(default_factory=lambda: LiteralObjectVar({}))
- right: ObjectVar = dataclasses.field(default_factory=lambda: LiteralObjectVar({}))
- def __init__(
- self,
- left: ObjectVar,
- right: ObjectVar,
- _var_data: VarData | None = None,
- ):
- """Initialize the object merge operation.
- Args:
- left: The left object to merge.
- right: The right object to merge.
- _var_data: Additional hooks and imports associated with the operation.
- """
- super(ObjectMergeOperation, self).__init__(
- _var_name="",
- _var_type=left._var_type,
- _var_data=ImmutableVarData.merge(_var_data),
- )
- object.__setattr__(self, "left", left)
- object.__setattr__(self, "right", right)
- object.__delattr__(self, "_var_name")
- @cached_property
- def _cached_var_name(self) -> str:
- """The name of the operation.
- Returns:
- The name of the operation.
- """
- return f"Object.assign({self.left._var_name}, {self.right._var_name})"
- def __getattr__(self, name):
- """Get an attribute of the operation.
- Args:
- name: The name of the attribute.
- Returns:
- The attribute of the operation.
- """
- if name == "_var_name":
- return self._cached_var_name
- return super(type(self), self).__getattr__(name)
- @cached_property
- def _cached_get_all_var_data(self) -> ImmutableVarData | None:
- """Get all VarData associated with the operation.
- Returns:
- The VarData of the components and all of its children.
- """
- return ImmutableVarData.merge(
- self.left._get_all_var_data(),
- self.right._get_all_var_data(),
- self._var_data,
- )
- def _get_all_var_data(self) -> ImmutableVarData | None:
- """Wrapper method for cached property.
- Returns:
- The VarData of the components and all of its children.
- """
- return self._cached_get_all_var_data
- @dataclasses.dataclass(
- eq=False,
- frozen=True,
- **{"slots": True} if sys.version_info >= (3, 10) else {},
- )
- class ObjectItemOperation(ImmutableVar):
- """Operation to get an item from an object."""
- value: ObjectVar = dataclasses.field(default_factory=lambda: LiteralObjectVar({}))
- key: Var | Any = dataclasses.field(default_factory=lambda: LiteralVar.create(None))
- def __init__(
- self,
- value: ObjectVar,
- key: Var | Any,
- _var_type: GenericType | None = None,
- _var_data: VarData | None = None,
- ):
- """Initialize the object item operation.
- Args:
- value: The value of the operation.
- key: The key to get from the object.
- _var_data: Additional hooks and imports associated with the operation.
- """
- super(ObjectItemOperation, self).__init__(
- _var_name="",
- _var_type=value._value_type() if _var_type is None else _var_type,
- _var_data=ImmutableVarData.merge(_var_data),
- )
- object.__setattr__(self, "value", value)
- object.__setattr__(
- self, "key", key if isinstance(key, Var) else LiteralVar.create(key)
- )
- object.__delattr__(self, "_var_name")
- @cached_property
- def _cached_var_name(self) -> str:
- """The name of the operation.
- Returns:
- The name of the operation.
- """
- return f"{str(self.value)}[{str(self.key)}]"
- def __getattr__(self, name):
- """Get an attribute of the operation.
- Args:
- name: The name of the attribute.
- Returns:
- The attribute of the operation.
- """
- if name == "_var_name":
- return self._cached_var_name
- return super(type(self), self).__getattr__(name)
- @cached_property
- def _cached_get_all_var_data(self) -> ImmutableVarData | None:
- """Get all VarData associated with the operation.
- Returns:
- The VarData of the components and all of its children.
- """
- return ImmutableVarData.merge(
- self.value._get_all_var_data(),
- self.key._get_all_var_data(),
- self._var_data,
- )
- def _get_all_var_data(self) -> ImmutableVarData | None:
- """Wrapper method for cached property.
- Returns:
- The VarData of the components and all of its children.
- """
- return self._cached_get_all_var_data
- @dataclasses.dataclass(
- eq=False,
- frozen=True,
- **{"slots": True} if sys.version_info >= (3, 10) else {},
- )
- class ToObjectOperation(ObjectVar):
- """Operation to convert a var to an object."""
- _original_var: Var = dataclasses.field(default_factory=lambda: LiteralObjectVar({}))
- def __init__(
- self,
- _original_var: Var,
- _var_type: Type = dict,
- _var_data: VarData | None = None,
- ):
- """Initialize the to object operation.
- Args:
- _original_var: The original var to convert.
- _var_type: The type of the var.
- _var_data: Additional hooks and imports associated with the operation.
- """
- super(ToObjectOperation, self).__init__(
- _var_name="",
- _var_type=_var_type,
- _var_data=ImmutableVarData.merge(_var_data),
- )
- object.__setattr__(self, "_original_var", _original_var)
- object.__delattr__(self, "_var_name")
- @cached_property
- def _cached_var_name(self) -> str:
- """The name of the operation.
- Returns:
- The name of the operation.
- """
- return str(self._original_var)
- def __getattr__(self, name):
- """Get an attribute of the operation.
- Args:
- name: The name of the attribute.
- Returns:
- The attribute of the operation.
- """
- if name == "_var_name":
- return self._cached_var_name
- return super(type(self), self).__getattr__(name)
- @cached_property
- def _cached_get_all_var_data(self) -> ImmutableVarData | None:
- """Get all VarData associated with the operation.
- Returns:
- The VarData of the components and all of its children.
- """
- return ImmutableVarData.merge(
- self._original_var._get_all_var_data(),
- self._var_data,
- )
- def _get_all_var_data(self) -> ImmutableVarData | None:
- """Wrapper method for cached property.
- Returns:
- The VarData of the components and all of its children.
- """
- return self._cached_get_all_var_data
|