[REF-3328] Implement __getitem__ for ArrayVar (#3705)
* half of the way there
* implement __getitem__ for array
* add some tests
* add fixes to pyright
* fix default factory
* implement array operations
* format code
* fix pyright issue
* give up
* add object operations
* add test for merge
* pyright 🥺
* use str isntead of _var_name
Co-authored-by: Masen Furer <m_github@0x26.net>
* wrong var_type
* make to much nicer
* add subclass checking
* enhance types
* use builtin list type
* improve typing even more
* i'm awaiting october
* use even better typing
* add hash, json, and guess type method
* fix pyright issues
* add a test and fix lots of errors
* fix pyright once again
* add type inference to list
---------
Co-authored-by: Masen Furer <m_github@0x26.net>
This commit is contained in:
parent
06833f6d8d
commit
1c400043c6
@ -1,9 +1,7 @@
|
|||||||
"""Experimental Immutable-Based Var System."""
|
"""Experimental Immutable-Based Var System."""
|
||||||
|
|
||||||
from .base import ImmutableVar as ImmutableVar
|
from .base import ImmutableVar as ImmutableVar
|
||||||
from .base import LiteralObjectVar as LiteralObjectVar
|
|
||||||
from .base import LiteralVar as LiteralVar
|
from .base import LiteralVar as LiteralVar
|
||||||
from .base import ObjectVar as ObjectVar
|
|
||||||
from .base import var_operation as var_operation
|
from .base import var_operation as var_operation
|
||||||
from .function import FunctionStringVar as FunctionStringVar
|
from .function import FunctionStringVar as FunctionStringVar
|
||||||
from .function import FunctionVar as FunctionVar
|
from .function import FunctionVar as FunctionVar
|
||||||
@ -12,6 +10,8 @@ from .number import BooleanVar as BooleanVar
|
|||||||
from .number import LiteralBooleanVar as LiteralBooleanVar
|
from .number import LiteralBooleanVar as LiteralBooleanVar
|
||||||
from .number import LiteralNumberVar as LiteralNumberVar
|
from .number import LiteralNumberVar as LiteralNumberVar
|
||||||
from .number import NumberVar as NumberVar
|
from .number import NumberVar as NumberVar
|
||||||
|
from .object import LiteralObjectVar as LiteralObjectVar
|
||||||
|
from .object import ObjectVar as ObjectVar
|
||||||
from .sequence import ArrayJoinOperation as ArrayJoinOperation
|
from .sequence import ArrayJoinOperation as ArrayJoinOperation
|
||||||
from .sequence import ArrayVar as ArrayVar
|
from .sequence import ArrayVar as ArrayVar
|
||||||
from .sequence import ConcatVarOperation as ConcatVarOperation
|
from .sequence import ConcatVarOperation as ConcatVarOperation
|
||||||
|
@ -4,18 +4,19 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import dataclasses
|
import dataclasses
|
||||||
import functools
|
import functools
|
||||||
|
import inspect
|
||||||
import sys
|
import sys
|
||||||
from typing import (
|
from typing import (
|
||||||
|
TYPE_CHECKING,
|
||||||
Any,
|
Any,
|
||||||
Callable,
|
Callable,
|
||||||
Dict,
|
|
||||||
Optional,
|
Optional,
|
||||||
Type,
|
Type,
|
||||||
TypeVar,
|
TypeVar,
|
||||||
Union,
|
overload,
|
||||||
)
|
)
|
||||||
|
|
||||||
from typing_extensions import ParamSpec
|
from typing_extensions import ParamSpec, get_origin
|
||||||
|
|
||||||
from reflex import constants
|
from reflex import constants
|
||||||
from reflex.base import Base
|
from reflex.base import Base
|
||||||
@ -30,6 +31,17 @@ from reflex.vars import (
|
|||||||
_global_vars,
|
_global_vars,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from .function import FunctionVar, ToFunctionOperation
|
||||||
|
from .number import (
|
||||||
|
BooleanVar,
|
||||||
|
NumberVar,
|
||||||
|
ToBooleanVarOperation,
|
||||||
|
ToNumberVarOperation,
|
||||||
|
)
|
||||||
|
from .object import ObjectVar, ToObjectOperation
|
||||||
|
from .sequence import ArrayVar, StringVar, ToArrayOperation, ToStringOperation
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass(
|
@dataclasses.dataclass(
|
||||||
eq=False,
|
eq=False,
|
||||||
@ -43,7 +55,7 @@ class ImmutableVar(Var):
|
|||||||
_var_name: str = dataclasses.field()
|
_var_name: str = dataclasses.field()
|
||||||
|
|
||||||
# The type of the var.
|
# The type of the var.
|
||||||
_var_type: Type = dataclasses.field(default=Any)
|
_var_type: types.GenericType = dataclasses.field(default=Any)
|
||||||
|
|
||||||
# Extra metadata associated with the Var
|
# Extra metadata associated with the Var
|
||||||
_var_data: Optional[ImmutableVarData] = dataclasses.field(default=None)
|
_var_data: Optional[ImmutableVarData] = dataclasses.field(default=None)
|
||||||
@ -265,9 +277,138 @@ class ImmutableVar(Var):
|
|||||||
# Encode the _var_data into the formatted output for tracking purposes.
|
# Encode the _var_data into the formatted output for tracking purposes.
|
||||||
return f"{constants.REFLEX_VAR_OPENING_TAG}{hashed_var}{constants.REFLEX_VAR_CLOSING_TAG}{self._var_name}"
|
return f"{constants.REFLEX_VAR_OPENING_TAG}{hashed_var}{constants.REFLEX_VAR_CLOSING_TAG}{self._var_name}"
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def to(
|
||||||
|
self, output: Type[NumberVar], var_type: type[int] | type[float] = float
|
||||||
|
) -> ToNumberVarOperation: ...
|
||||||
|
|
||||||
class ObjectVar(ImmutableVar):
|
@overload
|
||||||
"""Base class for immutable object vars."""
|
def to(self, output: Type[BooleanVar]) -> ToBooleanVarOperation: ...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def to(
|
||||||
|
self,
|
||||||
|
output: Type[ArrayVar],
|
||||||
|
var_type: type[list] | type[tuple] | type[set] = list,
|
||||||
|
) -> ToArrayOperation: ...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def to(self, output: Type[StringVar]) -> ToStringOperation: ...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def to(
|
||||||
|
self, output: Type[ObjectVar], var_type: types.GenericType = dict
|
||||||
|
) -> ToObjectOperation: ...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def to(
|
||||||
|
self, output: Type[FunctionVar], var_type: Type[Callable] = Callable
|
||||||
|
) -> ToFunctionOperation: ...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def to(
|
||||||
|
self, output: Type[OUTPUT], var_type: types.GenericType | None = None
|
||||||
|
) -> OUTPUT: ...
|
||||||
|
|
||||||
|
def to(
|
||||||
|
self, output: Type[OUTPUT], var_type: types.GenericType | None = None
|
||||||
|
) -> Var:
|
||||||
|
"""Convert the var to a different type.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
output: The output type.
|
||||||
|
var_type: The type of the var.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
TypeError: If the var_type is not a supported type for the output.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The converted var.
|
||||||
|
"""
|
||||||
|
from .number import (
|
||||||
|
BooleanVar,
|
||||||
|
NumberVar,
|
||||||
|
ToBooleanVarOperation,
|
||||||
|
ToNumberVarOperation,
|
||||||
|
)
|
||||||
|
|
||||||
|
fixed_type = (
|
||||||
|
var_type
|
||||||
|
if var_type is None or inspect.isclass(var_type)
|
||||||
|
else get_origin(var_type)
|
||||||
|
)
|
||||||
|
|
||||||
|
if issubclass(output, NumberVar):
|
||||||
|
if fixed_type is not None and not issubclass(fixed_type, (int, float)):
|
||||||
|
raise TypeError(
|
||||||
|
f"Unsupported type {var_type} for NumberVar. Must be int or float."
|
||||||
|
)
|
||||||
|
return ToNumberVarOperation(self, var_type or float)
|
||||||
|
if issubclass(output, BooleanVar):
|
||||||
|
return ToBooleanVarOperation(self)
|
||||||
|
|
||||||
|
from .sequence import ArrayVar, StringVar, ToArrayOperation, ToStringOperation
|
||||||
|
|
||||||
|
if issubclass(output, ArrayVar):
|
||||||
|
if fixed_type is not None and not issubclass(
|
||||||
|
fixed_type, (list, tuple, set)
|
||||||
|
):
|
||||||
|
raise TypeError(
|
||||||
|
f"Unsupported type {var_type} for ArrayVar. Must be list, tuple, or set."
|
||||||
|
)
|
||||||
|
return ToArrayOperation(self, var_type or list)
|
||||||
|
if issubclass(output, StringVar):
|
||||||
|
return ToStringOperation(self)
|
||||||
|
|
||||||
|
from .object import ObjectVar, ToObjectOperation
|
||||||
|
|
||||||
|
if issubclass(output, ObjectVar):
|
||||||
|
return ToObjectOperation(self, var_type or dict)
|
||||||
|
|
||||||
|
from .function import FunctionVar, ToFunctionOperation
|
||||||
|
|
||||||
|
if issubclass(output, FunctionVar):
|
||||||
|
if fixed_type is not None and not issubclass(fixed_type, Callable):
|
||||||
|
raise TypeError(
|
||||||
|
f"Unsupported type {var_type} for FunctionVar. Must be Callable."
|
||||||
|
)
|
||||||
|
return ToFunctionOperation(self, var_type or Callable)
|
||||||
|
|
||||||
|
return output(
|
||||||
|
_var_name=self._var_name,
|
||||||
|
_var_type=self._var_type if var_type is None else var_type,
|
||||||
|
_var_data=self._var_data,
|
||||||
|
)
|
||||||
|
|
||||||
|
def guess_type(self) -> ImmutableVar:
|
||||||
|
"""Guess the type of the var.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The guessed type.
|
||||||
|
"""
|
||||||
|
from .number import NumberVar
|
||||||
|
from .object import ObjectVar
|
||||||
|
from .sequence import ArrayVar, StringVar
|
||||||
|
|
||||||
|
if self._var_type is Any:
|
||||||
|
return self
|
||||||
|
|
||||||
|
var_type = self._var_type
|
||||||
|
|
||||||
|
fixed_type = var_type if inspect.isclass(var_type) else get_origin(var_type)
|
||||||
|
|
||||||
|
if issubclass(fixed_type, (int, float)):
|
||||||
|
return self.to(NumberVar, var_type)
|
||||||
|
if issubclass(fixed_type, dict):
|
||||||
|
return self.to(ObjectVar, var_type)
|
||||||
|
if issubclass(fixed_type, (list, tuple, set)):
|
||||||
|
return self.to(ArrayVar, var_type)
|
||||||
|
if issubclass(fixed_type, str):
|
||||||
|
return self.to(StringVar)
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
OUTPUT = TypeVar("OUTPUT", bound=ImmutableVar)
|
||||||
|
|
||||||
|
|
||||||
class LiteralVar(ImmutableVar):
|
class LiteralVar(ImmutableVar):
|
||||||
@ -299,6 +440,8 @@ class LiteralVar(ImmutableVar):
|
|||||||
if value is None:
|
if value is None:
|
||||||
return ImmutableVar.create_safe("null", _var_data=_var_data)
|
return ImmutableVar.create_safe("null", _var_data=_var_data)
|
||||||
|
|
||||||
|
from .object import LiteralObjectVar
|
||||||
|
|
||||||
if isinstance(value, Base):
|
if isinstance(value, Base):
|
||||||
return LiteralObjectVar(
|
return LiteralObjectVar(
|
||||||
value.dict(), _var_type=type(value), _var_data=_var_data
|
value.dict(), _var_type=type(value), _var_data=_var_data
|
||||||
@ -330,102 +473,15 @@ class LiteralVar(ImmutableVar):
|
|||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
"""Post-initialize the var."""
|
"""Post-initialize the var."""
|
||||||
|
|
||||||
|
def json(self) -> str:
|
||||||
|
"""Serialize the var to a JSON string.
|
||||||
|
|
||||||
@dataclasses.dataclass(
|
Raises:
|
||||||
eq=False,
|
NotImplementedError: If the method is not implemented.
|
||||||
frozen=True,
|
|
||||||
**{"slots": True} if sys.version_info >= (3, 10) else {},
|
|
||||||
)
|
|
||||||
class LiteralObjectVar(LiteralVar):
|
|
||||||
"""Base class for immutable literal object vars."""
|
|
||||||
|
|
||||||
_var_value: Dict[Union[Var, Any], Union[Var, Any]] = dataclasses.field(
|
|
||||||
default_factory=dict
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
_var_value: dict[Var | Any, Var | Any],
|
|
||||||
_var_type: Type = dict,
|
|
||||||
_var_data: VarData | None = None,
|
|
||||||
):
|
|
||||||
"""Initialize the object var.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
_var_value: The value of the var.
|
|
||||||
_var_data: Additional hooks and imports associated with the Var.
|
|
||||||
"""
|
"""
|
||||||
super(LiteralObjectVar, self).__init__(
|
raise NotImplementedError(
|
||||||
_var_name="",
|
"LiteralVar subclasses must implement the json method."
|
||||||
_var_type=_var_type,
|
|
||||||
_var_data=ImmutableVarData.merge(_var_data),
|
|
||||||
)
|
)
|
||||||
object.__setattr__(
|
|
||||||
self,
|
|
||||||
"_var_value",
|
|
||||||
_var_value,
|
|
||||||
)
|
|
||||||
object.__delattr__(self, "_var_name")
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
@functools.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()
|
|
||||||
]
|
|
||||||
)
|
|
||||||
+ " }"
|
|
||||||
)
|
|
||||||
|
|
||||||
@functools.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
|
|
||||||
|
|
||||||
|
|
||||||
P = ParamSpec("P")
|
P = ParamSpec("P")
|
||||||
|
@ -5,7 +5,7 @@ from __future__ import annotations
|
|||||||
import dataclasses
|
import dataclasses
|
||||||
import sys
|
import sys
|
||||||
from functools import cached_property
|
from functools import cached_property
|
||||||
from typing import Any, Callable, Optional, Tuple, Union
|
from typing import Any, Callable, Optional, Tuple, Type, Union
|
||||||
|
|
||||||
from reflex.experimental.vars.base import ImmutableVar, LiteralVar
|
from reflex.experimental.vars.base import ImmutableVar, LiteralVar
|
||||||
from reflex.vars import ImmutableVarData, Var, VarData
|
from reflex.vars import ImmutableVarData, Var, VarData
|
||||||
@ -212,3 +212,79 @@ class ArgsFunctionOperation(FunctionVar):
|
|||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
"""Post-initialize the var."""
|
"""Post-initialize the var."""
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass(
|
||||||
|
eq=False,
|
||||||
|
frozen=True,
|
||||||
|
**{"slots": True} if sys.version_info >= (3, 10) else {},
|
||||||
|
)
|
||||||
|
class ToFunctionOperation(FunctionVar):
|
||||||
|
"""Base class of converting a var to a function."""
|
||||||
|
|
||||||
|
_original_var: Var = dataclasses.field(
|
||||||
|
default_factory=lambda: LiteralVar.create(None)
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
original_var: Var,
|
||||||
|
_var_type: Type[Callable] = Callable,
|
||||||
|
_var_data: VarData | None = None,
|
||||||
|
) -> None:
|
||||||
|
"""Initialize the function with arguments var.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
original_var: The original var to convert to a function.
|
||||||
|
_var_type: The type of the function.
|
||||||
|
_var_data: Additional hooks and imports associated with the Var.
|
||||||
|
"""
|
||||||
|
super(ToFunctionOperation, self).__init__(
|
||||||
|
_var_name=f"",
|
||||||
|
_var_type=_var_type,
|
||||||
|
_var_data=ImmutableVarData.merge(_var_data),
|
||||||
|
)
|
||||||
|
object.__setattr__(self, "_original_var", original_var)
|
||||||
|
object.__delattr__(self, "_var_name")
|
||||||
|
|
||||||
|
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 str(self._original_var)
|
||||||
|
|
||||||
|
@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(
|
||||||
|
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
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import dataclasses
|
import dataclasses
|
||||||
|
import json
|
||||||
import sys
|
import sys
|
||||||
from functools import cached_property
|
from functools import cached_property
|
||||||
from typing import Any, Union
|
from typing import Any, Union
|
||||||
@ -1253,6 +1254,22 @@ class LiteralBooleanVar(LiteralVar, BooleanVar):
|
|||||||
)
|
)
|
||||||
object.__setattr__(self, "_var_value", _var_value)
|
object.__setattr__(self, "_var_value", _var_value)
|
||||||
|
|
||||||
|
def __hash__(self) -> int:
|
||||||
|
"""Hash the var.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The hash of the var.
|
||||||
|
"""
|
||||||
|
return hash((self.__class__.__name__, self._var_value))
|
||||||
|
|
||||||
|
def json(self) -> str:
|
||||||
|
"""Get the JSON representation of the var.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The JSON representation of the var.
|
||||||
|
"""
|
||||||
|
return "true" if self._var_value else "false"
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass(
|
@dataclasses.dataclass(
|
||||||
eq=False,
|
eq=False,
|
||||||
@ -1288,8 +1305,154 @@ class LiteralNumberVar(LiteralVar, NumberVar):
|
|||||||
Returns:
|
Returns:
|
||||||
The hash of the var.
|
The hash of the var.
|
||||||
"""
|
"""
|
||||||
return hash(self._var_value)
|
return hash((self.__class__.__name__, self._var_value))
|
||||||
|
|
||||||
|
def json(self) -> str:
|
||||||
|
"""Get the JSON representation of the var.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The JSON representation of the var.
|
||||||
|
"""
|
||||||
|
return json.dumps(self._var_value)
|
||||||
|
|
||||||
|
|
||||||
number_types = Union[NumberVar, LiteralNumberVar, int, float]
|
number_types = Union[NumberVar, LiteralNumberVar, int, float]
|
||||||
boolean_types = Union[BooleanVar, LiteralBooleanVar, bool]
|
boolean_types = Union[BooleanVar, LiteralBooleanVar, bool]
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass(
|
||||||
|
eq=False,
|
||||||
|
frozen=True,
|
||||||
|
**{"slots": True} if sys.version_info >= (3, 10) else {},
|
||||||
|
)
|
||||||
|
class ToNumberVarOperation(NumberVar):
|
||||||
|
"""Base class for immutable number vars that are the result of a number operation."""
|
||||||
|
|
||||||
|
_original_value: Var = dataclasses.field(
|
||||||
|
default_factory=lambda: LiteralNumberVar(0)
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
_original_value: Var,
|
||||||
|
_var_type: type[int] | type[float] = float,
|
||||||
|
_var_data: VarData | None = None,
|
||||||
|
):
|
||||||
|
"""Initialize the number var.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
_original_value: The original value.
|
||||||
|
_var_type: The type of the Var.
|
||||||
|
_var_data: Additional hooks and imports associated with the Var.
|
||||||
|
"""
|
||||||
|
super(ToNumberVarOperation, self).__init__(
|
||||||
|
_var_name="",
|
||||||
|
_var_type=_var_type,
|
||||||
|
_var_data=ImmutableVarData.merge(_var_data),
|
||||||
|
)
|
||||||
|
object.__setattr__(self, "_original_value", _original_value)
|
||||||
|
object.__delattr__(self, "_var_name")
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def _cached_var_name(self) -> str:
|
||||||
|
"""The name of the var.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The name of the var.
|
||||||
|
"""
|
||||||
|
return str(self._original_value)
|
||||||
|
|
||||||
|
def __getattr__(self, name: str) -> Any:
|
||||||
|
"""Get an attribute of the var.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name: The name of the attribute.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The attribute value.
|
||||||
|
"""
|
||||||
|
if name == "_var_name":
|
||||||
|
return self._cached_var_name
|
||||||
|
getattr(super(ToNumberVarOperation, self), name)
|
||||||
|
|
||||||
|
@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(
|
||||||
|
self._original_value._get_all_var_data(), self._var_data
|
||||||
|
)
|
||||||
|
|
||||||
|
def _get_all_var_data(self) -> ImmutableVarData | None:
|
||||||
|
return self._cached_get_all_var_data
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass(
|
||||||
|
eq=False,
|
||||||
|
frozen=True,
|
||||||
|
**{"slots": True} if sys.version_info >= (3, 10) else {},
|
||||||
|
)
|
||||||
|
class ToBooleanVarOperation(BooleanVar):
|
||||||
|
"""Base class for immutable boolean vars that are the result of a boolean operation."""
|
||||||
|
|
||||||
|
_original_value: Var = dataclasses.field(
|
||||||
|
default_factory=lambda: LiteralBooleanVar(False)
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
_original_value: Var,
|
||||||
|
_var_data: VarData | None = None,
|
||||||
|
):
|
||||||
|
"""Initialize the boolean var.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
_original_value: The original value.
|
||||||
|
_var_data: Additional hooks and imports associated with the Var.
|
||||||
|
"""
|
||||||
|
super(ToBooleanVarOperation, self).__init__(
|
||||||
|
_var_name="",
|
||||||
|
_var_type=bool,
|
||||||
|
_var_data=ImmutableVarData.merge(_var_data),
|
||||||
|
)
|
||||||
|
object.__setattr__(self, "_original_value", _original_value)
|
||||||
|
object.__delattr__(self, "_var_name")
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def _cached_var_name(self) -> str:
|
||||||
|
"""The name of the var.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The name of the var.
|
||||||
|
"""
|
||||||
|
return str(self._original_value)
|
||||||
|
|
||||||
|
def __getattr__(self, name: str) -> Any:
|
||||||
|
"""Get an attribute of the var.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name: The name of the attribute.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The attribute value.
|
||||||
|
"""
|
||||||
|
if name == "_var_name":
|
||||||
|
return self._cached_var_name
|
||||||
|
getattr(super(ToBooleanVarOperation, self), name)
|
||||||
|
|
||||||
|
@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(
|
||||||
|
self._original_value._get_all_var_data(), self._var_data
|
||||||
|
)
|
||||||
|
|
||||||
|
def _get_all_var_data(self) -> ImmutableVarData | None:
|
||||||
|
return self._cached_get_all_var_data
|
||||||
|
627
reflex/experimental/vars/object.py
Normal file
627
reflex/experimental/vars/object.py
Normal file
@ -0,0 +1,627 @@
|
|||||||
|
"""Classes for immutable object vars."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import dataclasses
|
||||||
|
import sys
|
||||||
|
import typing
|
||||||
|
from functools import cached_property
|
||||||
|
from typing import Any, Dict, List, Tuple, Type, Union
|
||||||
|
|
||||||
|
from reflex.experimental.vars.base import ImmutableVar, LiteralVar
|
||||||
|
from reflex.experimental.vars.sequence import ArrayVar, unionize
|
||||||
|
from reflex.vars import ImmutableVarData, Var, VarData
|
||||||
|
|
||||||
|
|
||||||
|
class ObjectVar(ImmutableVar):
|
||||||
|
"""Base class for immutable object vars."""
|
||||||
|
|
||||||
|
def _key_type(self) -> Type:
|
||||||
|
"""Get the type of the keys of the object.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The type of the keys of the object.
|
||||||
|
"""
|
||||||
|
return ImmutableVar
|
||||||
|
|
||||||
|
def _value_type(self) -> Type:
|
||||||
|
"""Get the type of the values of the object.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The type of the values of the object.
|
||||||
|
"""
|
||||||
|
return ImmutableVar
|
||||||
|
|
||||||
|
def keys(self) -> ObjectKeysOperation:
|
||||||
|
"""Get the keys of the object.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The keys of the object.
|
||||||
|
"""
|
||||||
|
return ObjectKeysOperation(self)
|
||||||
|
|
||||||
|
def values(self) -> ObjectValuesOperation:
|
||||||
|
"""Get the values of the object.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The values of the object.
|
||||||
|
"""
|
||||||
|
return ObjectValuesOperation(self)
|
||||||
|
|
||||||
|
def entries(self) -> ObjectEntriesOperation:
|
||||||
|
"""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)
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
def __getattr__(self, name) -> ObjectItemOperation:
|
||||||
|
"""Get an attribute of the var.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name: The name of the attribute.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The attribute of the var.
|
||||||
|
"""
|
||||||
|
return ObjectItemOperation(self, name)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass(
|
||||||
|
eq=False,
|
||||||
|
frozen=True,
|
||||||
|
**{"slots": True} if sys.version_info >= (3, 10) else {},
|
||||||
|
)
|
||||||
|
class LiteralObjectVar(LiteralVar, ObjectVar):
|
||||||
|
"""Base class for immutable literal object vars."""
|
||||||
|
|
||||||
|
_var_value: Dict[Union[Var, Any], Union[Var, Any]] = dataclasses.field(
|
||||||
|
default_factory=dict
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
_var_value: dict[Var | Any, Var | Any],
|
||||||
|
_var_type: 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=(
|
||||||
|
Dict[
|
||||||
|
unionize(*map(type, _var_value.keys())),
|
||||||
|
unionize(*map(type, _var_value.values())),
|
||||||
|
]
|
||||||
|
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_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(),
|
||||||
|
_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
|
File diff suppressed because it is too large
Load Diff
@ -1997,6 +1997,14 @@ class Var:
|
|||||||
"""
|
"""
|
||||||
return self._var_data
|
return self._var_data
|
||||||
|
|
||||||
|
def json(self) -> str:
|
||||||
|
"""Serialize the var to a JSON string.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
NotImplementedError: If the method is not implemented.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError("Var subclasses must implement the json method.")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _var_name_unwrapped(self) -> str:
|
def _var_name_unwrapped(self) -> str:
|
||||||
"""Get the var str without wrapping in curly braces.
|
"""Get the var str without wrapping in curly braces.
|
||||||
|
@ -151,6 +151,7 @@ class Var:
|
|||||||
def _var_full_name(self) -> str: ...
|
def _var_full_name(self) -> str: ...
|
||||||
def _var_set_state(self, state: Type[BaseState] | str) -> Any: ...
|
def _var_set_state(self, state: Type[BaseState] | str) -> Any: ...
|
||||||
def _get_all_var_data(self) -> VarData: ...
|
def _get_all_var_data(self) -> VarData: ...
|
||||||
|
def json(self) -> str: ...
|
||||||
|
|
||||||
@dataclass(eq=False)
|
@dataclass(eq=False)
|
||||||
class BaseVar(Var):
|
class BaseVar(Var):
|
||||||
|
@ -19,7 +19,13 @@ from reflex.experimental.vars.number import (
|
|||||||
LiteralNumberVar,
|
LiteralNumberVar,
|
||||||
NumberVar,
|
NumberVar,
|
||||||
)
|
)
|
||||||
from reflex.experimental.vars.sequence import ConcatVarOperation, LiteralStringVar
|
from reflex.experimental.vars.object import LiteralObjectVar
|
||||||
|
from reflex.experimental.vars.sequence import (
|
||||||
|
ArrayVar,
|
||||||
|
ConcatVarOperation,
|
||||||
|
LiteralArrayVar,
|
||||||
|
LiteralStringVar,
|
||||||
|
)
|
||||||
from reflex.state import BaseState
|
from reflex.state import BaseState
|
||||||
from reflex.utils.imports import ImportVar
|
from reflex.utils.imports import ImportVar
|
||||||
from reflex.vars import (
|
from reflex.vars import (
|
||||||
@ -881,7 +887,7 @@ def test_literal_var():
|
|||||||
)
|
)
|
||||||
assert (
|
assert (
|
||||||
str(complicated_var)
|
str(complicated_var)
|
||||||
== '[{ ["a"] : 1, ["b"] : 2, ["c"] : { ["d"] : 3, ["e"] : 4 } }, [1, 2, 3, 4], 9, "string", true, false, null, [1, 2, 3]]'
|
== '[({ ["a"] : 1, ["b"] : 2, ["c"] : ({ ["d"] : 3, ["e"] : 4 }) }), [1, 2, 3, 4], 9, "string", true, false, null, [1, 2, 3]]'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -898,7 +904,7 @@ def test_function_var():
|
|||||||
)
|
)
|
||||||
assert (
|
assert (
|
||||||
str(manual_addition_func.call(1, 2))
|
str(manual_addition_func.call(1, 2))
|
||||||
== '(((a, b) => ({ ["args"] : [a, b], ["result"] : a + b }))(1, 2))'
|
== '(((a, b) => (({ ["args"] : [a, b], ["result"] : a + b })))(1, 2))'
|
||||||
)
|
)
|
||||||
|
|
||||||
increment_func = addition_func(1)
|
increment_func = addition_func(1)
|
||||||
@ -935,7 +941,7 @@ def test_var_operation():
|
|||||||
def test_string_operations():
|
def test_string_operations():
|
||||||
basic_string = LiteralStringVar.create("Hello, World!")
|
basic_string = LiteralStringVar.create("Hello, World!")
|
||||||
|
|
||||||
assert str(basic_string.length()) == '"Hello, World!".length'
|
assert str(basic_string.length()) == '"Hello, World!".split("").length'
|
||||||
assert str(basic_string.lower()) == '"Hello, World!".toLowerCase()'
|
assert str(basic_string.lower()) == '"Hello, World!".toLowerCase()'
|
||||||
assert str(basic_string.upper()) == '"Hello, World!".toUpperCase()'
|
assert str(basic_string.upper()) == '"Hello, World!".toUpperCase()'
|
||||||
assert str(basic_string.strip()) == '"Hello, World!".trim()'
|
assert str(basic_string.strip()) == '"Hello, World!".trim()'
|
||||||
@ -972,6 +978,89 @@ def test_all_number_operations():
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_index_operation():
|
||||||
|
array_var = LiteralArrayVar([1, 2, 3, 4, 5])
|
||||||
|
assert str(array_var[0]) == "[1, 2, 3, 4, 5].at(0)"
|
||||||
|
assert str(array_var[1:2]) == "[1, 2, 3, 4, 5].slice(1, 2)"
|
||||||
|
assert (
|
||||||
|
str(array_var[1:4:2])
|
||||||
|
== "[1, 2, 3, 4, 5].slice(1, 4).filter((_, i) => i % 2 === 0)"
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
str(array_var[::-1])
|
||||||
|
== "[1, 2, 3, 4, 5].slice(0, [1, 2, 3, 4, 5].length).reverse().slice(undefined, undefined).filter((_, i) => i % 1 === 0)"
|
||||||
|
)
|
||||||
|
assert str(array_var.reverse()) == "[1, 2, 3, 4, 5].reverse()"
|
||||||
|
assert str(array_var[0].to(NumberVar) + 9) == "([1, 2, 3, 4, 5].at(0) + 9)"
|
||||||
|
|
||||||
|
|
||||||
|
def test_array_operations():
|
||||||
|
array_var = LiteralArrayVar.create([1, 2, 3, 4, 5])
|
||||||
|
|
||||||
|
assert str(array_var.length()) == "[1, 2, 3, 4, 5].length"
|
||||||
|
assert str(array_var.contains(3)) == "[1, 2, 3, 4, 5].includes(3)"
|
||||||
|
assert str(array_var.reverse()) == "[1, 2, 3, 4, 5].reverse()"
|
||||||
|
assert (
|
||||||
|
str(ArrayVar.range(10))
|
||||||
|
== "Array.from({ length: (10 - 0) / 1 }, (_, i) => 0 + i * 1)"
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
str(ArrayVar.range(1, 10))
|
||||||
|
== "Array.from({ length: (10 - 1) / 1 }, (_, i) => 1 + i * 1)"
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
str(ArrayVar.range(1, 10, 2))
|
||||||
|
== "Array.from({ length: (10 - 1) / 2 }, (_, i) => 1 + i * 2)"
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
str(ArrayVar.range(1, 10, -1))
|
||||||
|
== "Array.from({ length: (10 - 1) / -1 }, (_, i) => 1 + i * -1)"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_object_operations():
|
||||||
|
object_var = LiteralObjectVar({"a": 1, "b": 2, "c": 3})
|
||||||
|
|
||||||
|
assert (
|
||||||
|
str(object_var.keys()) == 'Object.keys(({ ["a"] : 1, ["b"] : 2, ["c"] : 3 }))'
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
str(object_var.values())
|
||||||
|
== 'Object.values(({ ["a"] : 1, ["b"] : 2, ["c"] : 3 }))'
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
str(object_var.entries())
|
||||||
|
== 'Object.entries(({ ["a"] : 1, ["b"] : 2, ["c"] : 3 }))'
|
||||||
|
)
|
||||||
|
assert str(object_var.a) == '({ ["a"] : 1, ["b"] : 2, ["c"] : 3 })["a"]'
|
||||||
|
assert str(object_var["a"]) == '({ ["a"] : 1, ["b"] : 2, ["c"] : 3 })["a"]'
|
||||||
|
assert (
|
||||||
|
str(object_var.merge(LiteralObjectVar({"c": 4, "d": 5})))
|
||||||
|
== 'Object.assign(({ ["a"] : 1, ["b"] : 2, ["c"] : 3 }), ({ ["c"] : 4, ["d"] : 5 }))'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_type_chains():
|
||||||
|
object_var = LiteralObjectVar({"a": 1, "b": 2, "c": 3})
|
||||||
|
assert object_var._var_type is Dict[str, int]
|
||||||
|
assert (object_var.keys()._var_type, object_var.values()._var_type) == (
|
||||||
|
List[str],
|
||||||
|
List[int],
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
str(object_var.keys()[0].upper()) # type: ignore
|
||||||
|
== 'Object.keys(({ ["a"] : 1, ["b"] : 2, ["c"] : 3 })).at(0).toUpperCase()'
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
str(object_var.entries()[1][1] - 1) # type: ignore
|
||||||
|
== '(Object.entries(({ ["a"] : 1, ["b"] : 2, ["c"] : 3 })).at(1).at(1) - 1)'
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
str(object_var["c"] + object_var["b"]) # type: ignore
|
||||||
|
== '(({ ["a"] : 1, ["b"] : 2, ["c"] : 3 })["c"] + ({ ["a"] : 1, ["b"] : 2, ["c"] : 3 })["b"])'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_retrival():
|
def test_retrival():
|
||||||
var_without_data = ImmutableVar.create("test")
|
var_without_data = ImmutableVar.create("test")
|
||||||
assert var_without_data is not None
|
assert var_without_data is not None
|
||||||
|
Loading…
Reference in New Issue
Block a user