add immutable var class (#3607)
* add immutable var class * add missing docs * override _replace * fix type imports * override create as well * remove deprecated properties and arguments * remove unused code in ImmutableVar * fix namespace issue * no Self in 3.8
This commit is contained in:
parent
59752d0cde
commit
046c0f9760
@ -8,6 +8,7 @@ from reflex.components.sonner.toast import toast as toast
|
|||||||
|
|
||||||
from ..utils.console import warn
|
from ..utils.console import warn
|
||||||
from . import hooks as hooks
|
from . import hooks as hooks
|
||||||
|
from . import vars as vars
|
||||||
from .assets import asset as asset
|
from .assets import asset as asset
|
||||||
from .client_state import ClientStateVar as ClientStateVar
|
from .client_state import ClientStateVar as ClientStateVar
|
||||||
from .layout import layout as layout
|
from .layout import layout as layout
|
||||||
@ -42,6 +43,7 @@ _x = ExperimentalNamespace(
|
|||||||
asset=asset,
|
asset=asset,
|
||||||
client_state=ClientStateVar.create,
|
client_state=ClientStateVar.create,
|
||||||
hooks=hooks,
|
hooks=hooks,
|
||||||
|
vars=vars,
|
||||||
layout=layout,
|
layout=layout,
|
||||||
progress=progress,
|
progress=progress,
|
||||||
PropsBase=PropsBase,
|
PropsBase=PropsBase,
|
||||||
|
3
reflex/experimental/vars/__init__.py
Normal file
3
reflex/experimental/vars/__init__.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
"""Experimental Immutable-Based Var System."""
|
||||||
|
|
||||||
|
from .base import ImmutableVar as ImmutableVar
|
158
reflex/experimental/vars/base.py
Normal file
158
reflex/experimental/vars/base.py
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
"""Collection of base classes."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import dataclasses
|
||||||
|
import sys
|
||||||
|
from typing import Any, Optional, Type
|
||||||
|
|
||||||
|
from reflex.utils import serializers, types
|
||||||
|
from reflex.utils.exceptions import VarTypeError
|
||||||
|
from reflex.vars import Var, VarData, _extract_var_data
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass(
|
||||||
|
eq=False,
|
||||||
|
frozen=True,
|
||||||
|
**{"slots": True} if sys.version_info >= (3, 10) else {},
|
||||||
|
)
|
||||||
|
class ImmutableVar(Var):
|
||||||
|
"""Base class for immutable vars."""
|
||||||
|
|
||||||
|
# The name of the var.
|
||||||
|
_var_name: str = dataclasses.field()
|
||||||
|
|
||||||
|
# The type of the var.
|
||||||
|
_var_type: Type = dataclasses.field(default=Any)
|
||||||
|
|
||||||
|
# Extra metadata associated with the Var
|
||||||
|
_var_data: Optional[VarData] = dataclasses.field(default=None)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _var_is_local(self) -> bool:
|
||||||
|
"""Whether this is a local javascript variable.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
False
|
||||||
|
"""
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _var_is_string(self) -> bool:
|
||||||
|
"""Whether the var is a string literal.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
False
|
||||||
|
"""
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _var_full_name_needs_state_prefix(self) -> bool:
|
||||||
|
"""Whether the full name of the var needs a _var_state prefix.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
False
|
||||||
|
"""
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _replace(self, merge_var_data=None, **kwargs: Any):
|
||||||
|
"""Make a copy of this Var with updated fields.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
merge_var_data: VarData to merge into the existing VarData.
|
||||||
|
**kwargs: Var fields to update.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A new ImmutableVar with the updated fields overwriting the corresponding fields in this Var.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
TypeError: If _var_is_local, _var_is_string, or _var_full_name_needs_state_prefix is not None.
|
||||||
|
"""
|
||||||
|
if kwargs.get("_var_is_local", False) is not False:
|
||||||
|
raise TypeError(
|
||||||
|
"The _var_is_local argument is not supported for ImmutableVar."
|
||||||
|
)
|
||||||
|
|
||||||
|
if kwargs.get("_var_is_string", False) is not False:
|
||||||
|
raise TypeError(
|
||||||
|
"The _var_is_string argument is not supported for ImmutableVar."
|
||||||
|
)
|
||||||
|
|
||||||
|
if kwargs.get("_var_full_name_needs_state_prefix", False) is not False:
|
||||||
|
raise TypeError(
|
||||||
|
"The _var_full_name_needs_state_prefix argument is not supported for ImmutableVar."
|
||||||
|
)
|
||||||
|
|
||||||
|
field_values = dict(
|
||||||
|
_var_name=kwargs.pop("_var_name", self._var_name),
|
||||||
|
_var_type=kwargs.pop("_var_type", self._var_type),
|
||||||
|
_var_data=VarData.merge(
|
||||||
|
kwargs.get("_var_data", self._var_data), merge_var_data
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return ImmutableVar(**field_values)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create(
|
||||||
|
cls,
|
||||||
|
value: Any,
|
||||||
|
_var_is_local: bool | None = None,
|
||||||
|
_var_is_string: bool | None = None,
|
||||||
|
_var_data: VarData | None = None,
|
||||||
|
) -> Var | None:
|
||||||
|
"""Create a var from a value.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value: The value to create the var from.
|
||||||
|
_var_is_local: Whether the var is local. Deprecated.
|
||||||
|
_var_is_string: Whether the var is a string literal. Deprecated.
|
||||||
|
_var_data: Additional hooks and imports associated with the Var.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The var.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
VarTypeError: If the value is JSON-unserializable.
|
||||||
|
TypeError: If _var_is_local or _var_is_string is not None.
|
||||||
|
"""
|
||||||
|
if _var_is_local is not None:
|
||||||
|
raise TypeError(
|
||||||
|
"The _var_is_local argument is not supported for ImmutableVar."
|
||||||
|
)
|
||||||
|
|
||||||
|
if _var_is_string is not None:
|
||||||
|
raise TypeError(
|
||||||
|
"The _var_is_string argument is not supported for ImmutableVar."
|
||||||
|
)
|
||||||
|
|
||||||
|
from reflex.utils import format
|
||||||
|
|
||||||
|
# Check for none values.
|
||||||
|
if value is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# If the value is already a var, do nothing.
|
||||||
|
if isinstance(value, Var):
|
||||||
|
return value
|
||||||
|
|
||||||
|
# Try to pull the imports and hooks from contained values.
|
||||||
|
if not isinstance(value, str):
|
||||||
|
_var_data = VarData.merge(*_extract_var_data(value), _var_data)
|
||||||
|
|
||||||
|
# Try to serialize the value.
|
||||||
|
type_ = type(value)
|
||||||
|
if type_ in types.JSONType:
|
||||||
|
name = value
|
||||||
|
else:
|
||||||
|
name, _serialized_type = serializers.serialize(value, get_type=True)
|
||||||
|
if name is None:
|
||||||
|
raise VarTypeError(
|
||||||
|
f"No JSON serializer found for var {value} of type {type_}."
|
||||||
|
)
|
||||||
|
name = name if isinstance(name, str) else format.json_dumps(name)
|
||||||
|
|
||||||
|
return ImmutableVar(
|
||||||
|
_var_name=name,
|
||||||
|
_var_type=type_,
|
||||||
|
_var_data=_var_data,
|
||||||
|
)
|
Loading…
Reference in New Issue
Block a user