-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
[REF-3227] implement more literal vars #3687
[REF-3227] implement more literal vars #3687
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think LiteralVar.create
should have a kind of dispatch table based on the type, that would allow us to write LiteralVar.create(["test", 43, [1, 2, 3], {"a": {"b": "c"}}])
and get back a LiteralArrayVar
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made some local changes while testing that you may want to consider
diff --git a/reflex/experimental/vars/base.py b/reflex/experimental/vars/base.py
index fce1a958..61a972be 100644
--- a/reflex/experimental/vars/base.py
+++ b/reflex/experimental/vars/base.py
@@ -10,6 +10,7 @@ from functools import cached_property
from typing import Any, Optional, Tuple, Type
from reflex import constants
+from reflex.base import Base
from reflex.constants.base import REFLEX_VAR_CLOSING_TAG, REFLEX_VAR_OPENING_TAG
from reflex.utils import serializers, types
from reflex.utils.exceptions import VarTypeError
@@ -279,6 +280,55 @@ class FunctionVar(ImmutableVar):
class LiteralVar(ImmutableVar):
"""Base class for immutable literal vars."""
+ @classmethod
+ def create(
+ cls,
+ value: Any,
+ _var_data: VarData | None = None,
+ ) -> LiteralVar:
+ """Create a var from a value.
+
+ Args:
+ value: The value to create the var from.
+ _var_data: Additional hooks and imports associated with the Var.
+
+ Returns:
+ The var.
+ """
+ # Eventually this needs to replace the @rx.serializer API to allow custom types to be converted to Var.
+ if isinstance(value, str):
+ return LiteralStringVar.create(value, _var_data=_var_data)
+ if isinstance(value, bool):
+ return LiteralBooleanVar.create(value, _var_data=_var_data)
+ if isinstance(value, (int, float)):
+ return LiteralNumberVar.create(value, _var_data=_var_data)
+ if isinstance(value, dict):
+ return LiteralObjectVar.create(value, _var_data=_var_data)
+ if isinstance(value, Base):
+ return LiteralObjectVar.create(value.dict(), _var_type=type(value), _var_data=_var_data)
+ if isinstance(value, (list, tuple, set)):
+ return LiteralArrayVar.create(value, _var_data=_var_data)
+ raise TypeError(f"Unsupported type {type(value)} for LiteralVar.")
+
+ @classmethod
+ def create_safe(
+ cls,
+ value: Any,
+ _var_data: VarData | None = None,
+ ) -> LiteralVar:
+ """Create a var from a value, asserting that it is not None.
+
+ Args:
+ value: The value to create the var from.
+ _var_data: Additional hooks and imports associated with the Var.
+
+ Returns:
+ The var.
+ """
+ var = cls.create(value, _var_data=_var_data)
+ assert var is not None
+ return var
+
def __post_init__(self):
"""Post-initialize the var."""
@@ -341,6 +391,7 @@ class LiteralStringVar(LiteralVar):
if serialized_data[1:].isnumeric():
# This is a global immutable var.
var = _global_vars[int(serialized_data)]
+ # XXX: either assert var._var_type is `str` or use JSON.stringify to implicitly convert.
strings_and_vals.append(var)
value = value[(end + len(var._var_name)) :]
else:
@@ -412,7 +463,7 @@ class ConcatVarOperation(StringVar):
"""
if name == "_var_name":
return self._cached_var_name
- return getattr(super(), name)
+ return super(type(self), self).__getattr__(name)
@cached_property
def _cached_var_name(self) -> str:
@@ -546,6 +597,7 @@ class LiteralObjectVar(LiteralVar):
def __init__(
self,
_var_value: dict[str, Var],
+ _var_type: Type | None = None,
_var_data: VarData | None = None,
):
"""Initialize the object var.
@@ -556,7 +608,7 @@ class LiteralObjectVar(LiteralVar):
"""
super(LiteralObjectVar, self).__init__(
_var_name="",
- _var_type=dict,
+ _var_type=_var_type or dict,
_var_data=ImmutableVarData.merge(_var_data),
)
object.__setattr__(self, "_var_value", tuple(_var_value.items()))
@@ -573,7 +625,7 @@ class LiteralObjectVar(LiteralVar):
"""
if name == "_var_name":
return self._cached_var_name
- return getattr(super(), name)
+ return super(type(self), self).__getattr__(name)
@cached_property
def _cached_var_name(self) -> str:
@@ -585,7 +637,13 @@ class LiteralObjectVar(LiteralVar):
return (
"{ "
+ ", ".join(
- [str(key) + ": " + str(value) for key, value in self._var_value]
+ [
+ (
+ str(key if isinstance(key, Var) else LiteralVar.create_safe(key)) + ": " +
+ str(value if isinstance(value, Var) else LiteralVar.create_safe(value))
+ )
+ for key, value in self._var_value
+ ]
)
+ " }"
)
@@ -605,6 +663,7 @@ class LiteralObjectVar(LiteralVar):
def create(
cls,
value: dict[str, Var],
+ _var_type: Type | None = None,
_var_data: VarData | None = None,
) -> LiteralObjectVar:
"""Create a var from an object value.
@@ -618,6 +677,7 @@ class LiteralObjectVar(LiteralVar):
"""
return LiteralObjectVar(
_var_value=value,
+ _var_type=_var_type,
_var_data=_var_data,
)
@@ -662,7 +722,7 @@ class LiteralArrayVar(LiteralVar):
"""
if name == "_var_name":
return self._cached_var_name
- return getattr(super(), name)
+ return super(type(self), self).__getattr__(name)
@cached_property
def _cached_var_name(self) -> str:
@@ -671,7 +731,7 @@ class LiteralArrayVar(LiteralVar):
Returns:
The name of the var.
"""
- return "[" + ", ".join([str(element) for element in self._var_value]) + "]"
+ return "[" + ", ".join([str(element if isinstance(element, Var) else LiteralVar.create_safe(element)) for element in self._var_value]) + "]"
@cached_property
def _get_all_var_data(self) -> ImmutableVarData | None:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code looks good. At the very least though, we need test cases for each of the new types and the call operation and combinations there of (like passing a ConcatVarOperation to a FunctionStringVar)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
awesome thanks
implements number, boolean, object, and array