Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add immutable var class #3607

Merged
merged 9 commits into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions reflex/experimental/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from ..utils.console import warn
from . import hooks as hooks
from . import vars as vars
from .assets import asset as asset
from .client_state import ClientStateVar as ClientStateVar
from .layout import layout as layout
Expand Down Expand Up @@ -42,6 +43,7 @@ def toast(self):
asset=asset,
client_state=ClientStateVar.create,
hooks=hooks,
vars=vars,
adhami3310 marked this conversation as resolved.
Show resolved Hide resolved
layout=layout,
progress=progress,
PropsBase=PropsBase,
Expand Down
3 changes: 3 additions & 0 deletions reflex/experimental/vars/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"""Experimental Immutable-Based Var System."""
adhami3310 marked this conversation as resolved.
Show resolved Hide resolved

from .base import ImmutableVar as ImmutableVar
158 changes: 158 additions & 0 deletions reflex/experimental/vars/base.py
Original file line number Diff line number Diff line change
@@ -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