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

[REF-3227] implement more literal vars #3687

Conversation

adhami3310
Copy link
Member

implements number, boolean, object, and array

Copy link

linear bot commented Jul 18, 2024

@adhami3310 adhami3310 requested a review from masenf July 18, 2024 21:21
Copy link
Collaborator

@masenf masenf left a 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

reflex/experimental/vars/base.py Outdated Show resolved Hide resolved
reflex/experimental/vars/base.py Outdated Show resolved Hide resolved
reflex/experimental/vars/base.py Outdated Show resolved Hide resolved
Copy link
Collaborator

@masenf masenf left a 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:

@adhami3310 adhami3310 requested a review from masenf July 19, 2024 00:44
Copy link
Collaborator

@masenf masenf left a 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)

Copy link
Collaborator

@masenf masenf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

awesome thanks

@adhami3310 adhami3310 merged commit ea01631 into main Jul 22, 2024
47 checks passed
@adhami3310 adhami3310 deleted the khaleel/ref-3227-implement-literalnumbervar-and-literalbooleanvar branch July 22, 2024 19:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants