fully migrate vars into new system
This commit is contained in:
parent
ad14f38329
commit
de1fbdbe5b
@ -15,6 +15,8 @@ def VarOperations():
|
||||
from typing import Dict, List
|
||||
|
||||
import reflex as rx
|
||||
from reflex.ivars.base import LiteralVar
|
||||
from reflex.ivars.sequence import ArrayVar
|
||||
|
||||
class VarOperationState(rx.State):
|
||||
int_var1: int = 10
|
||||
@ -29,8 +31,8 @@ def VarOperations():
|
||||
str_var2: str = "second"
|
||||
str_var3: str = "ThIrD"
|
||||
str_var4: str = "a long string"
|
||||
dict1: Dict = {1: 2}
|
||||
dict2: Dict = {3: 4}
|
||||
dict1: Dict[int, int] = {1: 2}
|
||||
dict2: Dict[int, int] = {3: 4}
|
||||
html_str: str = "<div>hello</div>"
|
||||
|
||||
app = rx.App(state=rx.State)
|
||||
@ -547,29 +549,29 @@ def VarOperations():
|
||||
"second",
|
||||
query=[VarOperationState.str_var2],
|
||||
),
|
||||
rx.text(rx.Var.range(2, 5).join(","), id="list_join_range1"),
|
||||
rx.text(rx.Var.range(2, 10, 2).join(","), id="list_join_range2"),
|
||||
rx.text(rx.Var.range(5, 0, -1).join(","), id="list_join_range3"),
|
||||
rx.text(rx.Var.range(0, 3).join(","), id="list_join_range4"),
|
||||
rx.text(ArrayVar.range(2, 5).join(","), id="list_join_range1"),
|
||||
rx.text(ArrayVar.range(2, 10, 2).join(","), id="list_join_range2"),
|
||||
rx.text(ArrayVar.range(5, 0, -1).join(","), id="list_join_range3"),
|
||||
rx.text(ArrayVar.range(0, 3).join(","), id="list_join_range4"),
|
||||
rx.box(
|
||||
rx.foreach(
|
||||
rx.Var.range(0, 2),
|
||||
ArrayVar.range(0, 2),
|
||||
lambda x: rx.text(VarOperationState.list1[x], as_="p"),
|
||||
),
|
||||
id="foreach_list_arg",
|
||||
),
|
||||
rx.box(
|
||||
rx.foreach(
|
||||
rx.Var.range(0, 2),
|
||||
ArrayVar.range(0, 2),
|
||||
lambda x, ix: rx.text(VarOperationState.list1[ix], as_="p"),
|
||||
),
|
||||
id="foreach_list_ix",
|
||||
),
|
||||
rx.box(
|
||||
rx.foreach(
|
||||
rx.Var.create_safe(list(range(0, 3))).to(List[int]),
|
||||
LiteralVar.create(list(range(0, 3))).to(ArrayVar, List[int]),
|
||||
lambda x: rx.foreach(
|
||||
rx.Var.range(x),
|
||||
ArrayVar.range(x),
|
||||
lambda y: rx.text(VarOperationState.list1[y], as_="p"),
|
||||
),
|
||||
),
|
||||
@ -783,6 +785,7 @@ def test_var_operations(driver, var_operations: AppHarness):
|
||||
]
|
||||
|
||||
for tag, expected in tests:
|
||||
print(tag)
|
||||
assert driver.find_element(By.ID, tag).text == expected
|
||||
|
||||
# Highlight component with var query (does not plumb ID)
|
||||
|
@ -338,6 +338,7 @@ _SUBMODULES: set[str] = {
|
||||
"testing",
|
||||
"utils",
|
||||
"vars",
|
||||
"ivars",
|
||||
"config",
|
||||
"compiler",
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ from . import compiler as compiler
|
||||
from . import components as components
|
||||
from . import config as config
|
||||
from . import event as event
|
||||
from . import ivars as ivars
|
||||
from . import model as model
|
||||
from . import style as style
|
||||
from . import testing as testing
|
||||
|
@ -527,9 +527,10 @@ class App(MiddlewareMixin, LifespanMixin, Base):
|
||||
self._enable_state()
|
||||
else:
|
||||
for var in component._get_vars(include_children=True):
|
||||
if not var._var_data:
|
||||
var_data = var._get_all_var_data()
|
||||
if not var_data:
|
||||
continue
|
||||
if not var._var_data.state:
|
||||
if not var_data.state:
|
||||
continue
|
||||
self._enable_state()
|
||||
break
|
||||
|
@ -17,6 +17,7 @@ from reflex.components.component import (
|
||||
StatefulComponent,
|
||||
)
|
||||
from reflex.config import get_config
|
||||
from reflex.ivars.base import ImmutableVar
|
||||
from reflex.state import BaseState
|
||||
from reflex.style import SYSTEM_COLOR_MODE
|
||||
from reflex.utils.exec import is_prod_mode
|
||||
@ -80,7 +81,7 @@ def _compile_contexts(state: Optional[Type[BaseState]], theme: Component | None)
|
||||
The compiled context file.
|
||||
"""
|
||||
appearance = getattr(theme, "appearance", None)
|
||||
if appearance is None or Var.create_safe(appearance)._var_name == "inherit":
|
||||
if appearance is None or str(ImmutableVar.create_safe(appearance)) == "inherit":
|
||||
appearance = SYSTEM_COLOR_MODE
|
||||
|
||||
last_compiled_time = str(datetime.now())
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
from reflex.components.base.fragment import Fragment
|
||||
from reflex.components.component import Component
|
||||
from reflex.vars import Var
|
||||
from reflex.ivars.base import ImmutableVar
|
||||
|
||||
|
||||
class AppWrap(Fragment):
|
||||
@ -15,6 +15,4 @@ class AppWrap(Fragment):
|
||||
Returns:
|
||||
A new AppWrap component containing {children}.
|
||||
"""
|
||||
return super().create(
|
||||
Var.create("{children}", _var_is_local=False, _var_is_string=False)
|
||||
)
|
||||
return super().create(ImmutableVar.create("children"))
|
||||
|
@ -7,13 +7,14 @@ from typing import Any, Iterator
|
||||
from reflex.components.component import Component
|
||||
from reflex.components.tags import Tag
|
||||
from reflex.components.tags.tagless import Tagless
|
||||
from reflex.ivars.base import ImmutableVar
|
||||
from reflex.vars import Var
|
||||
|
||||
|
||||
class Bare(Component):
|
||||
"""A component with no tag."""
|
||||
|
||||
contents: Var[str]
|
||||
contents: Var[Any]
|
||||
|
||||
@classmethod
|
||||
def create(cls, contents: Any) -> Component:
|
||||
@ -25,6 +26,8 @@ class Bare(Component):
|
||||
Returns:
|
||||
The component.
|
||||
"""
|
||||
if isinstance(contents, ImmutableVar):
|
||||
return cls(contents=contents)
|
||||
if isinstance(contents, Var) and contents._var_data:
|
||||
contents = contents.to(str)
|
||||
else:
|
||||
@ -32,6 +35,8 @@ class Bare(Component):
|
||||
return cls(contents=contents) # type: ignore
|
||||
|
||||
def _render(self) -> Tag:
|
||||
if isinstance(self.contents, ImmutableVar):
|
||||
return Tagless(contents=f"{{{str(self.contents)}}}")
|
||||
return Tagless(contents=str(self.contents))
|
||||
|
||||
def _get_vars(self, include_children: bool = False) -> Iterator[Var]:
|
||||
|
@ -9,6 +9,8 @@ from reflex.components.component import Component
|
||||
from reflex.components.el import div, p
|
||||
from reflex.constants import Hooks, Imports
|
||||
from reflex.event import EventChain, EventHandler
|
||||
from reflex.ivars.base import ImmutableVar
|
||||
from reflex.ivars.function import FunctionVar
|
||||
from reflex.utils.imports import ImportVar
|
||||
from reflex.vars import Var
|
||||
|
||||
@ -20,14 +22,14 @@ class ErrorBoundary(Component):
|
||||
tag = "ErrorBoundary"
|
||||
|
||||
# Fired when the boundary catches an error.
|
||||
on_error: EventHandler[lambda error, info: [error, info]] = Var.create_safe( # type: ignore
|
||||
"logFrontendError", _var_is_string=False, _var_is_local=False
|
||||
).to(EventChain)
|
||||
on_error: EventHandler[lambda error, info: [error, info]] = ImmutableVar( # type: ignore
|
||||
"logFrontendError"
|
||||
).to(FunctionVar, EventChain)
|
||||
|
||||
# Rendered instead of the children when an error is caught.
|
||||
Fallback_component: Var[Component] = Var.create_safe(
|
||||
"Fallback", _var_is_string=False, _var_is_local=False
|
||||
).to(Component)
|
||||
Fallback_component: Var[Component] = ImmutableVar.create_safe("Fallback")._replace(
|
||||
_var_type=Component
|
||||
)
|
||||
|
||||
def add_imports(self) -> dict[str, list[ImportVar]]:
|
||||
"""Add imports for the component.
|
||||
@ -56,7 +58,7 @@ class ErrorBoundary(Component):
|
||||
fallback_container = div(
|
||||
p("Ooops...Unknown Reflex error has occured:"),
|
||||
p(
|
||||
Var.create("error.message", _var_is_local=False, _var_is_string=False),
|
||||
ImmutableVar.create("error.message"),
|
||||
color="red",
|
||||
),
|
||||
p("Please contact the support."),
|
||||
|
@ -9,6 +9,7 @@ from typing import Literal
|
||||
|
||||
from reflex.components.component import Component
|
||||
from reflex.event import EventHandler
|
||||
from reflex.ivars.base import LiteralVar
|
||||
from reflex.vars import Var
|
||||
|
||||
|
||||
@ -31,7 +32,7 @@ class Script(Component):
|
||||
|
||||
# When the script will execute: afterInteractive (defer-like behavior) | beforeInteractive | lazyOnload (async-like behavior)
|
||||
strategy: Var[Literal["afterInteractive", "beforeInteractive", "lazyOnload"]] = (
|
||||
Var.create_safe("afterInteractive", _var_is_string=True)
|
||||
LiteralVar.create("afterInteractive")
|
||||
)
|
||||
|
||||
# Triggered when the script is loading
|
||||
|
@ -6,6 +6,7 @@ from functools import lru_cache
|
||||
from typing import List, Literal
|
||||
|
||||
from reflex.components.component import Component
|
||||
from reflex.ivars.base import ImmutableVar
|
||||
from reflex.utils.imports import ImportDict, ImportVar
|
||||
from reflex.vars import Var
|
||||
|
||||
@ -66,9 +67,7 @@ class ChakraProvider(ChakraComponent):
|
||||
A new ChakraProvider component.
|
||||
"""
|
||||
return super().create(
|
||||
theme=Var.create(
|
||||
"extendTheme(theme)", _var_is_local=False, _var_is_string=False
|
||||
),
|
||||
theme=ImmutableVar.create("extendTheme(theme)"),
|
||||
)
|
||||
|
||||
def add_imports(self) -> ImportDict:
|
||||
|
@ -8,6 +8,7 @@ from reflex.components.chakra import (
|
||||
LiteralTagSize,
|
||||
)
|
||||
from reflex.event import EventHandler
|
||||
from reflex.ivars.base import LiteralVar
|
||||
from reflex.vars import Var
|
||||
|
||||
|
||||
@ -50,7 +51,7 @@ class Checkbox(ChakraComponent):
|
||||
name: Var[str]
|
||||
|
||||
# The value of the input field when checked (use is_checked prop for a bool)
|
||||
value: Var[str] = Var.create("true", _var_is_string=True) # type: ignore
|
||||
value: Var[str] = LiteralVar.create("true")
|
||||
|
||||
# The spacing between the checkbox and its label text (0.5rem)
|
||||
spacing: Var[str]
|
||||
|
@ -7,6 +7,7 @@ from typing import Any, Optional
|
||||
from reflex.components.chakra import ChakraComponent, LiteralImageLoading
|
||||
from reflex.components.component import Component
|
||||
from reflex.event import EventHandler
|
||||
from reflex.ivars.base import LiteralVar
|
||||
from reflex.vars import Var
|
||||
|
||||
|
||||
@ -70,5 +71,5 @@ class Image(ChakraComponent):
|
||||
"""
|
||||
src = props.get("src", None)
|
||||
if src is not None and not isinstance(src, (Var)):
|
||||
props["src"] = Var.create(value=src, _var_is_string=True)
|
||||
props["src"] = LiteralVar.create(value=src)
|
||||
return super().create(*children, **props)
|
||||
|
@ -43,11 +43,12 @@ from reflex.event import (
|
||||
call_event_handler,
|
||||
get_handler_args,
|
||||
)
|
||||
from reflex.ivars.base import ImmutableVar, LiteralVar
|
||||
from reflex.style import Style, format_as_emotion
|
||||
from reflex.utils import console, format, imports, types
|
||||
from reflex.utils.imports import ImportDict, ImportVar, ParsedImportDict, parse_imports
|
||||
from reflex.utils.serializers import serializer
|
||||
from reflex.vars import BaseVar, Var, VarData
|
||||
from reflex.vars import BaseVar, ImmutableVarData, Var, VarData
|
||||
|
||||
|
||||
class BaseComponent(Base, ABC):
|
||||
@ -320,9 +321,8 @@ class Component(BaseComponent, ABC):
|
||||
# Set default values for any props.
|
||||
if types._issubclass(field.type_, Var):
|
||||
field.required = False
|
||||
field.default = Var.create(
|
||||
field.default, _var_is_string=isinstance(field.default, str)
|
||||
)
|
||||
if field.default is not None:
|
||||
field.default = LiteralVar.create(field.default)
|
||||
elif types._issubclass(field.type_, EventHandler):
|
||||
field.required = False
|
||||
|
||||
@ -351,10 +351,7 @@ class Component(BaseComponent, ABC):
|
||||
"id": kwargs.get("id"),
|
||||
"children": children,
|
||||
**{
|
||||
prop: Var.create(
|
||||
kwargs[prop],
|
||||
_var_is_string=False if isinstance(kwargs[prop], str) else None,
|
||||
)
|
||||
prop: LiteralVar.create(kwargs[prop])
|
||||
for prop in self.get_initial_props()
|
||||
if prop in kwargs
|
||||
},
|
||||
@ -401,10 +398,10 @@ class Component(BaseComponent, ABC):
|
||||
passed_types = None
|
||||
try:
|
||||
# Try to create a var from the value.
|
||||
kwargs[key] = Var.create(
|
||||
value,
|
||||
_var_is_string=False if isinstance(value, str) else None,
|
||||
)
|
||||
if isinstance(value, Var):
|
||||
kwargs[key] = value
|
||||
else:
|
||||
kwargs[key] = LiteralVar.create(value)
|
||||
|
||||
# Check that the var type is not None.
|
||||
if kwargs[key] is None:
|
||||
@ -448,7 +445,6 @@ class Component(BaseComponent, ABC):
|
||||
raise TypeError(
|
||||
f"Invalid var passed for prop {type(self).__name__}.{key}, expected type {expected_type}, got value {value_name} of type {passed_types or passed_type}."
|
||||
)
|
||||
|
||||
# Check if the key is an event trigger.
|
||||
if key in component_specific_triggers:
|
||||
# Temporarily disable full control for event triggers.
|
||||
@ -692,9 +688,7 @@ class Component(BaseComponent, ABC):
|
||||
# Add ref to element if `id` is not None.
|
||||
ref = self.get_ref()
|
||||
if ref is not None:
|
||||
props["ref"] = Var.create(
|
||||
ref, _var_is_local=False, _var_is_string=False
|
||||
)
|
||||
props["ref"] = ImmutableVar.create(ref)
|
||||
else:
|
||||
props = props.copy()
|
||||
|
||||
@ -809,7 +803,7 @@ class Component(BaseComponent, ABC):
|
||||
else (
|
||||
Fragment.create(*child)
|
||||
if isinstance(child, tuple)
|
||||
else Bare.create(contents=Var.create(child, _var_is_string=True))
|
||||
else Bare.create(contents=LiteralVar.create(child))
|
||||
)
|
||||
)
|
||||
for child in children
|
||||
@ -936,7 +930,12 @@ class Component(BaseComponent, ABC):
|
||||
"""
|
||||
if isinstance(self.style, Var):
|
||||
return {"css": self.style}
|
||||
return {"css": Var.create(format_as_emotion(self.style))}
|
||||
emotion_style = format_as_emotion(self.style)
|
||||
return (
|
||||
{"css": LiteralVar.create(emotion_style)}
|
||||
if emotion_style is not None
|
||||
else {}
|
||||
)
|
||||
|
||||
def render(self) -> Dict:
|
||||
"""Render the component.
|
||||
@ -1091,10 +1090,10 @@ class Component(BaseComponent, ABC):
|
||||
# Style keeps track of its own VarData instance, so embed in a temp Var that is yielded.
|
||||
if isinstance(self.style, dict) and self.style or isinstance(self.style, Var):
|
||||
vars.append(
|
||||
BaseVar(
|
||||
ImmutableVar(
|
||||
_var_name="style",
|
||||
_var_type=str,
|
||||
_var_data=self.style._var_data,
|
||||
_var_data=ImmutableVarData.merge(self.style._var_data),
|
||||
)
|
||||
)
|
||||
|
||||
@ -1113,10 +1112,8 @@ class Component(BaseComponent, ABC):
|
||||
vars.append(comp_prop)
|
||||
elif isinstance(comp_prop, str):
|
||||
# Collapse VarData encoded in f-strings.
|
||||
var = Var.create_safe(
|
||||
comp_prop, _var_is_string=isinstance(comp_prop, str)
|
||||
)
|
||||
if var._var_data is not None:
|
||||
var = LiteralVar.create(comp_prop)
|
||||
if var._get_all_var_data() is not None:
|
||||
vars.append(var)
|
||||
|
||||
# Get Vars associated with children.
|
||||
@ -1358,8 +1355,9 @@ class Component(BaseComponent, ABC):
|
||||
event_imports = Imports.EVENTS if self.event_triggers else {}
|
||||
|
||||
# Collect imports from Vars used directly by this component.
|
||||
var_datas = [var._get_all_var_data() for var in self._get_vars()]
|
||||
var_imports = [
|
||||
var._var_data.imports for var in self._get_vars() if var._var_data
|
||||
var_data.imports for var_data in var_datas if var_data is not None
|
||||
]
|
||||
|
||||
added_import_dicts: list[ParsedImportDict] = []
|
||||
@ -1427,7 +1425,7 @@ class Component(BaseComponent, ABC):
|
||||
"""
|
||||
ref = self.get_ref()
|
||||
if ref is not None:
|
||||
return f"const {ref} = useRef(null); {str(Var.create_safe(ref, _var_is_string=False).as_ref())} = {ref};"
|
||||
return f"const {ref} = useRef(null); {str(ImmutableVar.create_safe(ref).as_ref())} = {ref};"
|
||||
|
||||
def _get_vars_hooks(self) -> dict[str, None]:
|
||||
"""Get the hooks required by vars referenced in this component.
|
||||
@ -1437,8 +1435,13 @@ class Component(BaseComponent, ABC):
|
||||
"""
|
||||
vars_hooks = {}
|
||||
for var in self._get_vars():
|
||||
if var._var_data:
|
||||
vars_hooks.update(var._var_data.hooks)
|
||||
var_data = var._get_all_var_data()
|
||||
if var_data is not None:
|
||||
vars_hooks.update(
|
||||
var_data.hooks
|
||||
if isinstance(var_data.hooks, dict)
|
||||
else {k: None for k in var_data.hooks}
|
||||
)
|
||||
return vars_hooks
|
||||
|
||||
def _get_events_hooks(self) -> dict[str, None]:
|
||||
@ -1487,11 +1490,12 @@ class Component(BaseComponent, ABC):
|
||||
|
||||
def extract_var_hooks(hook: Var):
|
||||
_imports = {}
|
||||
if hook._var_data is not None:
|
||||
for sub_hook in hook._var_data.hooks:
|
||||
var_data = VarData.merge(hook._get_all_var_data())
|
||||
if var_data is not None:
|
||||
for sub_hook in var_data.hooks:
|
||||
code[sub_hook] = {}
|
||||
if hook._var_data.imports:
|
||||
_imports = hook._var_data.imports
|
||||
if var_data.imports:
|
||||
_imports = var_data.imports
|
||||
if str(hook) in code:
|
||||
code[str(hook)] = imports.merge_imports(code[str(hook)], _imports)
|
||||
else:
|
||||
@ -1505,6 +1509,7 @@ class Component(BaseComponent, ABC):
|
||||
extract_var_hooks(hook)
|
||||
else:
|
||||
code[hook] = {}
|
||||
|
||||
return code
|
||||
|
||||
def _get_hooks(self) -> str | None:
|
||||
@ -1561,7 +1566,7 @@ class Component(BaseComponent, ABC):
|
||||
The ref name.
|
||||
"""
|
||||
# do not create a ref if the id is dynamic or unspecified
|
||||
if self.id is None or isinstance(self.id, BaseVar):
|
||||
if self.id is None or isinstance(self.id, (BaseVar, ImmutableVar)):
|
||||
return None
|
||||
return format.format_ref(self.id)
|
||||
|
||||
@ -1707,7 +1712,7 @@ class CustomComponent(Component):
|
||||
|
||||
# Handle subclasses of Base.
|
||||
if isinstance(value, Base):
|
||||
base_value = Var.create(value)
|
||||
base_value = LiteralVar.create(value)
|
||||
|
||||
# Track hooks and imports associated with Component instances.
|
||||
if base_value is not None and isinstance(value, Component):
|
||||
@ -1721,7 +1726,7 @@ class CustomComponent(Component):
|
||||
else:
|
||||
value = base_value
|
||||
else:
|
||||
value = Var.create(value, _var_is_string=isinstance(value, str))
|
||||
value = LiteralVar.create(value)
|
||||
|
||||
# Set the prop.
|
||||
self.props[format.to_camel_case(key)] = value
|
||||
@ -1800,19 +1805,19 @@ class CustomComponent(Component):
|
||||
"""
|
||||
return super()._render(props=self.props)
|
||||
|
||||
def get_prop_vars(self) -> List[BaseVar]:
|
||||
def get_prop_vars(self) -> List[ImmutableVar]:
|
||||
"""Get the prop vars.
|
||||
|
||||
Returns:
|
||||
The prop vars.
|
||||
"""
|
||||
return [
|
||||
BaseVar(
|
||||
ImmutableVar(
|
||||
_var_name=name,
|
||||
_var_type=(
|
||||
prop._var_type if types._isinstance(prop, Var) else type(prop)
|
||||
),
|
||||
)
|
||||
).guess_type()
|
||||
for name, prop in self.props.items()
|
||||
]
|
||||
|
||||
@ -1981,7 +1986,7 @@ class StatefulComponent(BaseComponent):
|
||||
if not should_memoize:
|
||||
# Determine if any Vars have associated data.
|
||||
for prop_var in component._get_vars():
|
||||
if prop_var._var_data:
|
||||
if prop_var._get_all_var_data():
|
||||
should_memoize = True
|
||||
break
|
||||
|
||||
@ -1996,7 +2001,7 @@ class StatefulComponent(BaseComponent):
|
||||
should_memoize = True
|
||||
break
|
||||
child = cls._child_var(child)
|
||||
if isinstance(child, Var) and child._var_data:
|
||||
if isinstance(child, Var) and child._get_all_var_data():
|
||||
should_memoize = True
|
||||
break
|
||||
|
||||
@ -2187,12 +2192,12 @@ class StatefulComponent(BaseComponent):
|
||||
# Calculate Var dependencies accessed by the handler for useCallback dep array.
|
||||
var_deps = ["addEvents", "Event"]
|
||||
for arg in event_args:
|
||||
if arg._var_data is None:
|
||||
if arg._get_all_var_data() is None:
|
||||
continue
|
||||
for hook in arg._var_data.hooks:
|
||||
for hook in arg._get_all_var_data().hooks:
|
||||
var_deps.extend(cls._get_hook_deps(hook))
|
||||
memo_var_data = VarData.merge(
|
||||
*[var._var_data for var in event_args],
|
||||
*[var._get_all_var_data() for var in event_args],
|
||||
VarData(
|
||||
imports={"react": [ImportVar(tag="useCallback")]},
|
||||
),
|
||||
@ -2200,7 +2205,7 @@ class StatefulComponent(BaseComponent):
|
||||
|
||||
# Store the memoized function name and hook code for this event trigger.
|
||||
trigger_memo[event_trigger] = (
|
||||
Var.create_safe(memo_name, _var_is_string=False)._replace(
|
||||
ImmutableVar.create_safe(memo_name)._replace(
|
||||
_var_type=EventChain, merge_var_data=memo_var_data
|
||||
),
|
||||
f"const {memo_name} = useCallback({rendered_chain}, [{', '.join(var_deps)}])",
|
||||
|
@ -19,47 +19,42 @@ from reflex.components.radix.themes.typography.text import Text
|
||||
from reflex.components.sonner.toast import Toaster, ToastProps
|
||||
from reflex.constants import Dirs, Hooks, Imports
|
||||
from reflex.constants.compiler import CompileVars
|
||||
from reflex.ivars.base import ImmutableVar, LiteralVar
|
||||
from reflex.ivars.function import FunctionStringVar
|
||||
from reflex.ivars.number import BooleanVar
|
||||
from reflex.ivars.sequence import LiteralArrayVar
|
||||
from reflex.utils.imports import ImportDict, ImportVar
|
||||
from reflex.utils.serializers import serialize
|
||||
from reflex.vars import Var, VarData
|
||||
from reflex.vars import ImmutableVarData, Var, VarData
|
||||
|
||||
connect_error_var_data: VarData = VarData( # type: ignore
|
||||
imports=Imports.EVENTS,
|
||||
hooks={Hooks.EVENTS: None},
|
||||
)
|
||||
|
||||
connect_errors: Var = Var.create_safe(
|
||||
connect_errors: Var = ImmutableVar.create_safe(
|
||||
value=CompileVars.CONNECT_ERROR,
|
||||
_var_is_local=True,
|
||||
_var_is_string=False,
|
||||
_var_data=connect_error_var_data,
|
||||
)
|
||||
|
||||
connection_error: Var = Var.create_safe(
|
||||
value="(connectErrors.length > 0) ? connectErrors[connectErrors.length - 1].message : ''",
|
||||
_var_is_local=False,
|
||||
_var_is_string=False,
|
||||
connection_error: Var = ImmutableVar.create_safe(
|
||||
value="((connectErrors.length > 0) ? connectErrors[connectErrors.length - 1].message : '')",
|
||||
_var_data=connect_error_var_data,
|
||||
)
|
||||
|
||||
connection_errors_count: Var = Var.create_safe(
|
||||
connection_errors_count: Var = ImmutableVar.create_safe(
|
||||
value="connectErrors.length",
|
||||
_var_is_string=False,
|
||||
_var_is_local=False,
|
||||
_var_data=connect_error_var_data,
|
||||
)
|
||||
|
||||
has_connection_errors: Var = Var.create_safe(
|
||||
value="connectErrors.length > 0",
|
||||
_var_is_string=False,
|
||||
has_connection_errors: Var = ImmutableVar.create_safe(
|
||||
value="(connectErrors.length > 0)",
|
||||
_var_data=connect_error_var_data,
|
||||
).to(bool)
|
||||
).to(BooleanVar)
|
||||
|
||||
has_too_many_connection_errors: Var = Var.create_safe(
|
||||
value="connectErrors.length >= 2",
|
||||
_var_is_string=False,
|
||||
has_too_many_connection_errors: Var = ImmutableVar.create_safe(
|
||||
value="(connectErrors.length >= 2)",
|
||||
_var_data=connect_error_var_data,
|
||||
).to(bool)
|
||||
).to(BooleanVar)
|
||||
|
||||
|
||||
class WebsocketTargetURL(Bare):
|
||||
@ -77,13 +72,21 @@ class WebsocketTargetURL(Bare):
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def create(cls) -> Component:
|
||||
def create(cls) -> ImmutableVar:
|
||||
"""Create a websocket target URL component.
|
||||
|
||||
Returns:
|
||||
The websocket target URL component.
|
||||
"""
|
||||
return super().create(contents="{getBackendURL(env.EVENT).href}")
|
||||
return ImmutableVar(
|
||||
_var_name="getBackendURL(env.EVENT).href",
|
||||
_var_data=ImmutableVarData(
|
||||
imports={
|
||||
"/env.json": [ImportVar(tag="env", is_default=True)],
|
||||
f"/{Dirs.STATE_PATH}": [ImportVar(tag="getBackendURL")],
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def default_connection_error() -> list[str | Var | Component]:
|
||||
@ -112,24 +115,34 @@ class ConnectionToaster(Toaster):
|
||||
toast_id = "websocket-error"
|
||||
target_url = WebsocketTargetURL.create()
|
||||
props = ToastProps( # type: ignore
|
||||
description=Var.create(
|
||||
f"`Check if server is reachable at ${target_url}`",
|
||||
_var_is_string=False,
|
||||
_var_is_local=False,
|
||||
description=LiteralVar.create(
|
||||
f"Check if server is reachable at {target_url}",
|
||||
),
|
||||
close_button=True,
|
||||
duration=120000,
|
||||
id=toast_id,
|
||||
)
|
||||
hook = Var.create_safe(
|
||||
f"""
|
||||
const toast_props = {serialize(props)};
|
||||
const [userDismissed, setUserDismissed] = useState(false);
|
||||
useEffect(() => {{
|
||||
if ({has_too_many_connection_errors}) {{
|
||||
|
||||
individual_hooks = [
|
||||
f"const toast_props = {str(LiteralVar.create(props))};",
|
||||
f"const [userDismissed, setUserDismissed] = useState(false);",
|
||||
FunctionStringVar(
|
||||
"useEffect",
|
||||
_var_data=VarData(
|
||||
imports={
|
||||
"react": ["useEffect", "useState"],
|
||||
**dict(target_url._get_all_var_data().imports), # type: ignore
|
||||
}
|
||||
),
|
||||
).call(
|
||||
# TODO: This breaks the assumption that Vars are JS expressions
|
||||
ImmutableVar.create_safe(
|
||||
f"""
|
||||
() => {{
|
||||
if ({str(has_too_many_connection_errors)}) {{
|
||||
if (!userDismissed) {{
|
||||
toast.error(
|
||||
`Cannot connect to server: {connection_error}.`,
|
||||
`Cannot connect to server: ${{{connection_error}}}.`,
|
||||
{{...toast_props, onDismiss: () => setUserDismissed(true)}},
|
||||
)
|
||||
}}
|
||||
@ -137,20 +150,16 @@ useEffect(() => {{
|
||||
toast.dismiss("{toast_id}");
|
||||
setUserDismissed(false); // after reconnection reset dismissed state
|
||||
}}
|
||||
}}, [{connect_errors}]);""",
|
||||
_var_is_string=False,
|
||||
)
|
||||
imports: ImportDict = {
|
||||
"react": ["useEffect", "useState"],
|
||||
**target_url._get_imports(), # type: ignore
|
||||
}
|
||||
hook._var_data = VarData.merge(
|
||||
connect_errors._var_data,
|
||||
VarData(imports=imports),
|
||||
)
|
||||
}}
|
||||
"""
|
||||
),
|
||||
LiteralArrayVar([connect_errors]),
|
||||
),
|
||||
]
|
||||
|
||||
return [
|
||||
Hooks.EVENTS,
|
||||
hook,
|
||||
*individual_hooks,
|
||||
]
|
||||
|
||||
@classmethod
|
||||
@ -240,6 +249,7 @@ class WifiOffPulse(Icon):
|
||||
Returns:
|
||||
The icon component with default props applied.
|
||||
"""
|
||||
pulse_var = ImmutableVar.create("pulse")
|
||||
return super().create(
|
||||
"wifi_off",
|
||||
color=props.pop("color", "crimson"),
|
||||
@ -248,7 +258,7 @@ class WifiOffPulse(Icon):
|
||||
position=props.pop("position", "fixed"),
|
||||
bottom=props.pop("botton", "33px"),
|
||||
right=props.pop("right", "33px"),
|
||||
animation=Var.create(f"${{pulse}} 1s infinite", _var_is_string=True),
|
||||
animation=LiteralVar.create(f"{pulse_var} 1s infinite"),
|
||||
**props,
|
||||
)
|
||||
|
||||
|
@ -2,17 +2,17 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, Optional, Union, overload
|
||||
from typing import Any, Dict, Optional, overload
|
||||
|
||||
from reflex.components.base.fragment import Fragment
|
||||
from reflex.components.component import BaseComponent, Component, MemoizationLeaf
|
||||
from reflex.components.tags import CondTag, Tag
|
||||
from reflex.constants import Dirs
|
||||
from reflex.constants.colors import Color
|
||||
from reflex.ivars.base import ImmutableVar, LiteralVar
|
||||
from reflex.ivars.number import TernaryOperator
|
||||
from reflex.style import LIGHT_COLOR_MODE, resolved_color_mode
|
||||
from reflex.utils import format
|
||||
from reflex.utils.imports import ImportDict, ImportVar
|
||||
from reflex.vars import BaseVar, Var, VarData
|
||||
from reflex.vars import Var, VarData
|
||||
|
||||
_IS_TRUE_IMPORT: ImportDict = {
|
||||
f"/{Dirs.STATE_PATH}": [ImportVar(tag="isTrue")],
|
||||
@ -118,10 +118,10 @@ def cond(condition: Any, c1: Component) -> Component: ...
|
||||
|
||||
|
||||
@overload
|
||||
def cond(condition: Any, c1: Any, c2: Any) -> BaseVar: ...
|
||||
def cond(condition: Any, c1: Any, c2: Any) -> ImmutableVar: ...
|
||||
|
||||
|
||||
def cond(condition: Any, c1: Any, c2: Any = None):
|
||||
def cond(condition: Any, c1: Any, c2: Any = None) -> Component | ImmutableVar:
|
||||
"""Create a conditional component or Prop.
|
||||
|
||||
Args:
|
||||
@ -135,14 +135,8 @@ def cond(condition: Any, c1: Any, c2: Any = None):
|
||||
Raises:
|
||||
ValueError: If the arguments are invalid.
|
||||
"""
|
||||
var_datas: list[VarData | None] = [
|
||||
VarData( # type: ignore
|
||||
imports=_IS_TRUE_IMPORT,
|
||||
),
|
||||
]
|
||||
|
||||
# Convert the condition to a Var.
|
||||
cond_var = Var.create(condition)
|
||||
cond_var = LiteralVar.create(condition)
|
||||
assert cond_var is not None, "The condition must be set."
|
||||
|
||||
# If the first component is a component, create a Cond component.
|
||||
@ -151,8 +145,6 @@ def cond(condition: Any, c1: Any, c2: Any = None):
|
||||
c2, BaseComponent
|
||||
), "Both arguments must be components."
|
||||
return Cond.create(cond_var, c1, c2)
|
||||
if isinstance(c1, Var):
|
||||
var_datas.append(c1._var_data)
|
||||
|
||||
# Otherwise, create a conditional Var.
|
||||
# Check that the second argument is valid.
|
||||
@ -160,37 +152,20 @@ def cond(condition: Any, c1: Any, c2: Any = None):
|
||||
raise ValueError("Both arguments must be props.")
|
||||
if c2 is None:
|
||||
raise ValueError("For conditional vars, the second argument must be set.")
|
||||
if isinstance(c2, Var):
|
||||
var_datas.append(c2._var_data)
|
||||
|
||||
def create_var(cond_part):
|
||||
return Var.create_safe(
|
||||
cond_part,
|
||||
_var_is_string=isinstance(cond_part, (str, Color)),
|
||||
)
|
||||
return LiteralVar.create_safe(cond_part)
|
||||
|
||||
# convert the truth and false cond parts into vars so the _var_data can be obtained.
|
||||
c1 = create_var(c1)
|
||||
c2 = create_var(c2)
|
||||
var_datas.extend([c1._var_data, c2._var_data])
|
||||
|
||||
c1_type = c1._var_type if isinstance(c1, Var) else type(c1)
|
||||
c2_type = c2._var_type if isinstance(c2, Var) else type(c2)
|
||||
|
||||
var_type = c1_type if c1_type == c2_type else Union[c1_type, c2_type]
|
||||
|
||||
# Create the conditional var.
|
||||
return cond_var._replace(
|
||||
_var_name=format.format_cond(
|
||||
cond=cond_var._var_full_name,
|
||||
true_value=c1,
|
||||
false_value=c2,
|
||||
is_prop=True,
|
||||
),
|
||||
_var_type=var_type,
|
||||
_var_is_local=False,
|
||||
_var_full_name_needs_state_prefix=False,
|
||||
merge_var_data=VarData.merge(*var_datas),
|
||||
return TernaryOperator(
|
||||
condition=cond_var,
|
||||
if_true=c1,
|
||||
if_false=c2,
|
||||
_var_data=VarData(imports=_IS_TRUE_IMPORT),
|
||||
)
|
||||
|
||||
|
||||
@ -205,7 +180,7 @@ def color_mode_cond(light: Any, dark: Any = None) -> Var | Component:
|
||||
The conditional component or prop.
|
||||
"""
|
||||
return cond(
|
||||
resolved_color_mode == Var.create(LIGHT_COLOR_MODE, _var_is_string=True),
|
||||
resolved_color_mode == LiteralVar.create(LIGHT_COLOR_MODE),
|
||||
light,
|
||||
dark,
|
||||
)
|
||||
|
@ -9,6 +9,7 @@ from reflex.components.base.fragment import Fragment
|
||||
from reflex.components.component import Component
|
||||
from reflex.components.tags import IterTag
|
||||
from reflex.constants import MemoizationMode
|
||||
from reflex.ivars.base import ImmutableVar
|
||||
from reflex.state import ComponentState
|
||||
from reflex.utils import console
|
||||
from reflex.vars import Var
|
||||
@ -61,7 +62,7 @@ class Foreach(Component):
|
||||
deprecation_version="0.5.0",
|
||||
removal_version="0.6.0",
|
||||
)
|
||||
iterable = Var.create_safe(iterable, _var_is_string=False)
|
||||
iterable = ImmutableVar.create_safe(iterable)
|
||||
if iterable._var_type == Any:
|
||||
raise ForeachVarError(
|
||||
f"Could not foreach over var `{iterable._var_full_name}` of type Any. "
|
||||
|
@ -5,8 +5,8 @@ from typing import Any, Dict, List, Optional, Tuple, Union
|
||||
|
||||
from reflex.components.base import Fragment
|
||||
from reflex.components.component import BaseComponent, Component, MemoizationLeaf
|
||||
from reflex.components.core.colors import Color
|
||||
from reflex.components.tags import MatchTag, Tag
|
||||
from reflex.ivars.base import LiteralVar
|
||||
from reflex.style import Style
|
||||
from reflex.utils import format, types
|
||||
from reflex.utils.exceptions import MatchTypeError
|
||||
@ -68,7 +68,7 @@ class Match(MemoizationLeaf):
|
||||
Raises:
|
||||
ValueError: If the condition is not provided.
|
||||
"""
|
||||
match_cond_var = Var.create(cond, _var_is_string=isinstance(cond, str))
|
||||
match_cond_var = LiteralVar.create(cond)
|
||||
|
||||
if match_cond_var is None:
|
||||
raise ValueError("The condition must be set")
|
||||
@ -118,12 +118,11 @@ class Match(MemoizationLeaf):
|
||||
The case element Var.
|
||||
"""
|
||||
_var_data = case_element._var_data if isinstance(case_element, Style) else None # type: ignore
|
||||
case_element = Var.create(
|
||||
case_element,
|
||||
_var_is_string=isinstance(case_element, (str, Color)),
|
||||
)
|
||||
case_element = LiteralVar.create(case_element)
|
||||
if _var_data is not None:
|
||||
case_element._var_data = VarData.merge(case_element._var_data, _var_data) # type: ignore
|
||||
case_element._var_data = VarData.merge(
|
||||
case_element._get_all_var_data(), _var_data
|
||||
) # type: ignore
|
||||
return case_element
|
||||
|
||||
@classmethod
|
||||
|
@ -12,6 +12,7 @@ from reflex.components.radix.themes.components.button import Button
|
||||
from reflex.components.radix.themes.layout.box import Box
|
||||
from reflex.constants.colors import Color
|
||||
from reflex.event import set_clipboard
|
||||
from reflex.ivars.base import ImmutableVar, LiteralVar
|
||||
from reflex.style import Style
|
||||
from reflex.utils import format
|
||||
from reflex.utils.imports import ImportDict, ImportVar
|
||||
@ -484,7 +485,7 @@ class CodeBlock(Component):
|
||||
if children:
|
||||
props["code"] = children[0]
|
||||
if not isinstance(props["code"], Var):
|
||||
props["code"] = Var.create(props["code"], _var_is_string=True)
|
||||
props["code"] = LiteralVar.create(props["code"])
|
||||
|
||||
# Create the component.
|
||||
code_block = super().create(
|
||||
@ -505,10 +506,8 @@ class CodeBlock(Component):
|
||||
out = super()._render()
|
||||
predicate, qmark, value = self.theme._var_name.partition("?")
|
||||
out.add_props(
|
||||
style=Var.create(
|
||||
style=ImmutableVar.create(
|
||||
format.to_camel_case(f"{predicate}{qmark}{value.replace('`', '')}"),
|
||||
_var_is_local=False,
|
||||
_var_is_string=False,
|
||||
)
|
||||
).remove_props("theme", "code")
|
||||
if self.code is not None:
|
||||
|
@ -9,6 +9,7 @@ from reflex.base import Base
|
||||
from reflex.components.component import Component, NoSSRComponent
|
||||
from reflex.components.literals import LiteralRowMarker
|
||||
from reflex.event import EventHandler
|
||||
from reflex.ivars.base import ImmutableVar
|
||||
from reflex.utils import console, format, types
|
||||
from reflex.utils.imports import ImportDict, ImportVar
|
||||
from reflex.utils.serializers import serializer
|
||||
@ -293,9 +294,7 @@ class DataEditor(NoSSRComponent):
|
||||
|
||||
# Define the name of the getData callback associated with this component and assign to get_cell_content.
|
||||
data_callback = f"getData_{editor_id}"
|
||||
self.get_cell_content = Var.create(
|
||||
data_callback, _var_is_local=False, _var_is_string=False
|
||||
) # type: ignore
|
||||
self.get_cell_content = ImmutableVar.create(data_callback) # type: ignore
|
||||
|
||||
code = [f"function {data_callback}([col, row])" "{"]
|
||||
|
||||
|
@ -11,13 +11,14 @@ from reflex.components.el.element import Element
|
||||
from reflex.components.tags.tag import Tag
|
||||
from reflex.constants import Dirs, EventTriggers
|
||||
from reflex.event import EventChain, EventHandler
|
||||
from reflex.ivars.base import ImmutableVar
|
||||
from reflex.utils.format import format_event_chain
|
||||
from reflex.utils.imports import ImportDict
|
||||
from reflex.vars import BaseVar, Var
|
||||
|
||||
from .base import BaseHTML
|
||||
|
||||
FORM_DATA = Var.create("form_data", _var_is_string=False)
|
||||
FORM_DATA = ImmutableVar.create("form_data")
|
||||
HANDLE_SUBMIT_JS_JINJA2 = Environment().from_string(
|
||||
"""
|
||||
const handleSubmit_{{ handle_submit_unique_name }} = useCallback((ev) => {
|
||||
|
@ -17,6 +17,7 @@ from reflex.components.radix.themes.typography.heading import Heading
|
||||
from reflex.components.radix.themes.typography.link import Link
|
||||
from reflex.components.radix.themes.typography.text import Text
|
||||
from reflex.components.tags.tag import Tag
|
||||
from reflex.ivars.base import LiteralVar
|
||||
from reflex.utils import types
|
||||
from reflex.utils.imports import ImportDict, ImportVar
|
||||
from reflex.vars import Var
|
||||
@ -287,7 +288,7 @@ class Markdown(Component):
|
||||
function {self._get_component_map_name()} () {{
|
||||
{formatted_hooks}
|
||||
return (
|
||||
{str(Var.create(self.format_component_map()))}
|
||||
{str(LiteralVar.create(self.format_component_map()))}
|
||||
)
|
||||
}}
|
||||
"""
|
||||
|
@ -3,6 +3,7 @@
|
||||
from typing import Any, Literal, Optional, Union
|
||||
|
||||
from reflex.event import EventHandler
|
||||
from reflex.ivars.base import LiteralVar
|
||||
from reflex.utils import types
|
||||
from reflex.vars import Var
|
||||
|
||||
@ -104,6 +105,6 @@ class Image(NextComponent):
|
||||
|
||||
src = props.get("src", None)
|
||||
if src is not None and not isinstance(src, (Var)):
|
||||
props["src"] = Var.create(value=src, _var_is_string=True)
|
||||
props["src"] = LiteralVar.create(src)
|
||||
|
||||
return super().create(*children, **props)
|
||||
|
@ -11,6 +11,7 @@ from reflex.components.lucide.icon import Icon
|
||||
from reflex.components.radix.primitives.base import RadixPrimitiveComponent
|
||||
from reflex.components.radix.themes.base import LiteralAccentColor, LiteralRadius
|
||||
from reflex.event import EventHandler
|
||||
from reflex.ivars.base import LiteralVar
|
||||
from reflex.style import Style
|
||||
from reflex.vars import Var, get_uuid_string_var
|
||||
|
||||
@ -464,14 +465,12 @@ to {
|
||||
Returns:
|
||||
The style of the component.
|
||||
"""
|
||||
slideDown = Var.create(
|
||||
slideDown = LiteralVar.create(
|
||||
f"${{slideDown}} var(--animation-duration) var(--animation-easing)",
|
||||
_var_is_string=True,
|
||||
)
|
||||
|
||||
slideUp = Var.create(
|
||||
slideUp = LiteralVar.create(
|
||||
f"${{slideUp}} var(--animation-duration) var(--animation-easing)",
|
||||
_var_is_string=True,
|
||||
)
|
||||
|
||||
return {
|
||||
|
@ -7,6 +7,7 @@ from typing import Any, Dict, Literal
|
||||
from reflex.components import Component
|
||||
from reflex.components.tags import Tag
|
||||
from reflex.config import get_config
|
||||
from reflex.ivars.base import ImmutableVar
|
||||
from reflex.utils.imports import ImportDict, ImportVar
|
||||
from reflex.vars import Var
|
||||
|
||||
@ -230,10 +231,8 @@ class Theme(RadixThemesComponent):
|
||||
def _render(self, props: dict[str, Any] | None = None) -> Tag:
|
||||
tag = super()._render(props)
|
||||
tag.add_props(
|
||||
css=Var.create(
|
||||
"{{...theme.styles.global[':root'], ...theme.styles.global.body}}",
|
||||
_var_is_local=False,
|
||||
_var_is_string=False,
|
||||
css=ImmutableVar.create(
|
||||
f"{{...theme.styles.global[':root'], ...theme.styles.global.body}}"
|
||||
),
|
||||
)
|
||||
return tag
|
||||
|
@ -17,7 +17,6 @@ rx.text(
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import dataclasses
|
||||
from typing import Literal, get_args
|
||||
|
||||
from reflex.components.component import BaseComponent
|
||||
@ -25,6 +24,7 @@ from reflex.components.core.cond import Cond, color_mode_cond, cond
|
||||
from reflex.components.lucide.icon import Icon
|
||||
from reflex.components.radix.themes.components.dropdown_menu import dropdown_menu
|
||||
from reflex.components.radix.themes.components.switch import Switch
|
||||
from reflex.ivars.base import ImmutableVar
|
||||
from reflex.style import (
|
||||
LIGHT_COLOR_MODE,
|
||||
color_mode,
|
||||
@ -33,7 +33,7 @@ from reflex.style import (
|
||||
toggle_color_mode,
|
||||
)
|
||||
from reflex.utils import console
|
||||
from reflex.vars import BaseVar, Var
|
||||
from reflex.vars import Var
|
||||
|
||||
from .components.icon_button import IconButton
|
||||
|
||||
@ -195,7 +195,7 @@ class ColorModeSwitch(Switch):
|
||||
)
|
||||
|
||||
|
||||
class ColorModeNamespace(BaseVar):
|
||||
class ColorModeNamespace(ImmutableVar):
|
||||
"""Namespace for color mode components."""
|
||||
|
||||
icon = staticmethod(ColorModeIcon.create)
|
||||
@ -204,5 +204,7 @@ class ColorModeNamespace(BaseVar):
|
||||
|
||||
|
||||
color_mode = color_mode_var_and_namespace = ColorModeNamespace(
|
||||
**dataclasses.asdict(color_mode)
|
||||
_var_name=color_mode._var_name,
|
||||
_var_type=color_mode._var_type,
|
||||
_var_data=color_mode._var_data,
|
||||
)
|
||||
|
@ -10,6 +10,8 @@ from reflex.components.core.breakpoints import Responsive
|
||||
from reflex.components.radix.themes.layout.flex import Flex
|
||||
from reflex.components.radix.themes.typography.text import Text
|
||||
from reflex.event import EventHandler
|
||||
from reflex.ivars.base import ImmutableVar, LiteralVar
|
||||
from reflex.ivars.function import JSON_STRINGIFY
|
||||
from reflex.vars import Var
|
||||
|
||||
from ..base import (
|
||||
@ -147,28 +149,24 @@ class HighLevelRadioGroup(RadixThemesComponent):
|
||||
color_scheme = props.pop("color_scheme", None)
|
||||
default_value = props.pop("default_value", "")
|
||||
|
||||
default_value = Var.create(default_value, _var_is_string=True)
|
||||
default_value = LiteralVar.create(default_value)
|
||||
|
||||
# convert only non-strings to json(JSON.stringify) so quotes are not rendered
|
||||
# for string literal types.
|
||||
if isinstance(default_value, str) or (
|
||||
isinstance(default_value, Var) and default_value._var_type is str
|
||||
):
|
||||
default_value = Var.create(default_value, _var_is_string=True) # type: ignore
|
||||
default_value = LiteralVar.create(default_value) # type: ignore
|
||||
else:
|
||||
default_value = (
|
||||
Var.create(default_value, _var_is_string=False)
|
||||
.to_string() # type: ignore
|
||||
._replace(_var_is_local=False)
|
||||
)
|
||||
default_value = JSON_STRINGIFY.call(ImmutableVar.create(default_value))
|
||||
|
||||
def radio_group_item(value: str | Var) -> Component:
|
||||
item_value = Var.create(value, _var_is_string=False) # type: ignore
|
||||
item_value = rx.cond(
|
||||
item_value._type() == str, # type: ignore
|
||||
item_value,
|
||||
item_value.to_string()._replace(_var_is_local=False), # type: ignore
|
||||
)._replace(_var_type=str)
|
||||
JSON_STRINGIFY.call(item_value), # type: ignore
|
||||
)
|
||||
|
||||
return Text.create(
|
||||
Flex.create(
|
||||
|
@ -6,7 +6,8 @@ import inspect
|
||||
from typing import TYPE_CHECKING, Any, Callable, List, Tuple, Type, Union, get_args
|
||||
|
||||
from reflex.components.tags.tag import Tag
|
||||
from reflex.vars import BaseVar, Var
|
||||
from reflex.ivars.base import ImmutableVar
|
||||
from reflex.vars import Var
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from reflex.components.component import Component
|
||||
@ -53,10 +54,10 @@ class IterTag(Tag):
|
||||
Returns:
|
||||
The index var.
|
||||
"""
|
||||
return BaseVar(
|
||||
return ImmutableVar(
|
||||
_var_name=self.index_var_name,
|
||||
_var_type=int,
|
||||
)
|
||||
).guess_type()
|
||||
|
||||
def get_arg_var(self) -> Var:
|
||||
"""Get the arg var for the tag (with curly braces).
|
||||
@ -66,10 +67,10 @@ class IterTag(Tag):
|
||||
Returns:
|
||||
The arg var.
|
||||
"""
|
||||
return BaseVar(
|
||||
return ImmutableVar(
|
||||
_var_name=self.arg_var_name,
|
||||
_var_type=self.get_iterable_var_type(),
|
||||
)
|
||||
).guess_type()
|
||||
|
||||
def get_index_var_arg(self) -> Var:
|
||||
"""Get the index var for the tag (without curly braces).
|
||||
@ -79,11 +80,10 @@ class IterTag(Tag):
|
||||
Returns:
|
||||
The index var.
|
||||
"""
|
||||
return BaseVar(
|
||||
return ImmutableVar(
|
||||
_var_name=self.index_var_name,
|
||||
_var_type=int,
|
||||
_var_is_local=True,
|
||||
)
|
||||
).guess_type()
|
||||
|
||||
def get_arg_var_arg(self) -> Var:
|
||||
"""Get the arg var for the tag (without curly braces).
|
||||
@ -93,11 +93,10 @@ class IterTag(Tag):
|
||||
Returns:
|
||||
The arg var.
|
||||
"""
|
||||
return BaseVar(
|
||||
return ImmutableVar(
|
||||
_var_name=self.arg_var_name,
|
||||
_var_type=self.get_iterable_var_type(),
|
||||
_var_is_local=True,
|
||||
)
|
||||
).guess_type()
|
||||
|
||||
def render_component(self) -> Component:
|
||||
"""Render the component.
|
||||
|
@ -6,6 +6,7 @@ from typing import Any, Dict, List, Optional, Set, Tuple, Union
|
||||
|
||||
from reflex.base import Base
|
||||
from reflex.event import EventChain
|
||||
from reflex.ivars.base import ImmutableVar, LiteralVar
|
||||
from reflex.utils import format, types
|
||||
from reflex.vars import Var
|
||||
|
||||
@ -41,7 +42,7 @@ class Tag(Base):
|
||||
# Convert any props to vars.
|
||||
if "props" in kwargs:
|
||||
kwargs["props"] = {
|
||||
name: Var.create(value, _var_is_string=False)
|
||||
name: ImmutableVar.create(value)
|
||||
for name, value in kwargs["props"].items()
|
||||
}
|
||||
super().__init__(*args, **kwargs)
|
||||
@ -63,14 +64,12 @@ class Tag(Base):
|
||||
Returns:
|
||||
The tag with the props added.
|
||||
"""
|
||||
from reflex.components.core.colors import Color
|
||||
|
||||
self.props.update(
|
||||
{
|
||||
format.to_camel_case(name, allow_hyphens=True): prop
|
||||
if types._isinstance(prop, Union[EventChain, dict])
|
||||
else Var.create(
|
||||
prop, _var_is_string=isinstance(prop, Color)
|
||||
format.to_camel_case(name, allow_hyphens=True): (
|
||||
prop
|
||||
if types._isinstance(prop, Union[EventChain, dict])
|
||||
else LiteralVar.create(prop)
|
||||
) # rx.color is always a string
|
||||
for name, prop in kwargs.items()
|
||||
if self.is_valid_prop(prop)
|
||||
|
@ -18,9 +18,12 @@ from typing import (
|
||||
|
||||
from reflex import constants
|
||||
from reflex.base import Base
|
||||
from reflex.ivars.base import ImmutableVar, LiteralVar
|
||||
from reflex.ivars.function import FunctionStringVar, FunctionVar
|
||||
from reflex.ivars.object import ObjectVar
|
||||
from reflex.utils import format
|
||||
from reflex.utils.types import ArgsSpec
|
||||
from reflex.vars import BaseVar, Var
|
||||
from reflex.vars import ImmutableVarData, Var
|
||||
|
||||
try:
|
||||
from typing import Annotated
|
||||
@ -186,7 +189,7 @@ class EventHandler(EventActionsMixin):
|
||||
|
||||
# Get the function args.
|
||||
fn_args = inspect.getfullargspec(self.fn).args[1:]
|
||||
fn_args = (Var.create_safe(arg, _var_is_string=False) for arg in fn_args)
|
||||
fn_args = (ImmutableVar.create_safe(arg) for arg in fn_args)
|
||||
|
||||
# Construct the payload.
|
||||
values = []
|
||||
@ -197,7 +200,7 @@ class EventHandler(EventActionsMixin):
|
||||
|
||||
# Otherwise, convert to JSON.
|
||||
try:
|
||||
values.append(Var.create(arg, _var_is_string=isinstance(arg, str)))
|
||||
values.append(LiteralVar.create(arg))
|
||||
except TypeError as e:
|
||||
raise EventHandlerTypeError(
|
||||
f"Arguments to event handlers must be Vars or JSON-serializable. Got {arg} of type {type(arg)}."
|
||||
@ -264,13 +267,13 @@ class EventSpec(EventActionsMixin):
|
||||
|
||||
# Get the remaining unfilled function args.
|
||||
fn_args = inspect.getfullargspec(self.handler.fn).args[1 + len(self.args) :]
|
||||
fn_args = (Var.create_safe(arg, _var_is_string=False) for arg in fn_args)
|
||||
fn_args = (ImmutableVar.create_safe(arg) for arg in fn_args)
|
||||
|
||||
# Construct the payload.
|
||||
values = []
|
||||
for arg in args:
|
||||
try:
|
||||
values.append(Var.create(arg, _var_is_string=isinstance(arg, str)))
|
||||
values.append(LiteralVar.create(arg))
|
||||
except TypeError as e:
|
||||
raise EventHandlerTypeError(
|
||||
f"Arguments to event handlers must be Vars or JSON-serializable. Got {arg} of type {type(arg)}."
|
||||
@ -388,15 +391,16 @@ class FileUpload(Base):
|
||||
upload_id = self.upload_id or DEFAULT_UPLOAD_ID
|
||||
spec_args = [
|
||||
(
|
||||
Var.create_safe("files", _var_is_string=False),
|
||||
Var.create_safe(
|
||||
f"filesById[{Var.create_safe(upload_id, _var_is_string=True)._var_name_unwrapped}]",
|
||||
_var_is_string=False,
|
||||
)._replace(_var_data=upload_files_context_var_data),
|
||||
ImmutableVar.create_safe("files"),
|
||||
ImmutableVar(
|
||||
_var_name="filesById",
|
||||
_var_type=dict[str, Any],
|
||||
_var_data=ImmutableVarData.merge(upload_files_context_var_data),
|
||||
).to(ObjectVar)[LiteralVar.create_safe(upload_id)],
|
||||
),
|
||||
(
|
||||
Var.create_safe("upload_id", _var_is_string=False),
|
||||
Var.create_safe(upload_id, _var_is_string=True),
|
||||
ImmutableVar.create_safe("upload_id"),
|
||||
LiteralVar.create_safe(upload_id),
|
||||
),
|
||||
]
|
||||
if self.on_upload_progress is not None:
|
||||
@ -424,11 +428,10 @@ class FileUpload(Base):
|
||||
formatted_chain = str(format.format_prop(on_upload_progress_chain))
|
||||
spec_args.append(
|
||||
(
|
||||
Var.create_safe("on_upload_progress", _var_is_string=False),
|
||||
BaseVar(
|
||||
_var_name=formatted_chain.strip("{}"),
|
||||
_var_type=EventChain,
|
||||
),
|
||||
ImmutableVar.create_safe("on_upload_progress"),
|
||||
FunctionStringVar(
|
||||
formatted_chain.strip("{}"),
|
||||
).to(FunctionVar, EventChain),
|
||||
),
|
||||
)
|
||||
return EventSpec(
|
||||
@ -465,8 +468,8 @@ def server_side(name: str, sig: inspect.Signature, **kwargs) -> EventSpec:
|
||||
handler=EventHandler(fn=fn),
|
||||
args=tuple(
|
||||
(
|
||||
Var.create_safe(k, _var_is_string=False),
|
||||
Var.create_safe(v, _var_is_string=isinstance(v, str)),
|
||||
ImmutableVar.create_safe(k),
|
||||
LiteralVar.create(v),
|
||||
)
|
||||
for k, v in kwargs.items()
|
||||
),
|
||||
@ -542,7 +545,7 @@ def set_focus(ref: str) -> EventSpec:
|
||||
return server_side(
|
||||
"_set_focus",
|
||||
get_fn_signature(set_focus),
|
||||
ref=Var.create_safe(format.format_ref(ref), _var_is_string=True),
|
||||
ref=ImmutableVar.create_safe(format.format_ref(ref)),
|
||||
)
|
||||
|
||||
|
||||
@ -573,7 +576,7 @@ def set_value(ref: str, value: Any) -> EventSpec:
|
||||
return server_side(
|
||||
"_set_value",
|
||||
get_fn_signature(set_value),
|
||||
ref=Var.create_safe(format.format_ref(ref), _var_is_string=True),
|
||||
ref=ImmutableVar.create_safe(format.format_ref(ref)),
|
||||
value=value,
|
||||
)
|
||||
|
||||
@ -757,11 +760,13 @@ def _callback_arg_spec(eval_result):
|
||||
|
||||
def call_script(
|
||||
javascript_code: str | Var[str],
|
||||
callback: EventSpec
|
||||
| EventHandler
|
||||
| Callable
|
||||
| List[EventSpec | EventHandler | Callable]
|
||||
| None = None,
|
||||
callback: (
|
||||
EventSpec
|
||||
| EventHandler
|
||||
| Callable
|
||||
| List[EventSpec | EventHandler | Callable]
|
||||
| None
|
||||
) = None,
|
||||
) -> EventSpec:
|
||||
"""Create an event handler that executes arbitrary javascript code.
|
||||
|
||||
@ -865,10 +870,8 @@ def parse_args_spec(arg_spec: ArgsSpec):
|
||||
annotations = get_type_hints(arg_spec)
|
||||
return arg_spec(
|
||||
*[
|
||||
BaseVar(
|
||||
_var_name=f"_{l_arg}",
|
||||
_var_type=annotations.get(l_arg, FrontendEvent),
|
||||
_var_is_local=True,
|
||||
ImmutableVar(f"_{l_arg}").to(
|
||||
ObjectVar, annotations.get(l_arg, FrontendEvent)
|
||||
)
|
||||
for l_arg in spec.args
|
||||
]
|
||||
|
@ -8,7 +8,6 @@ from reflex.components.sonner.toast import toast as toast
|
||||
|
||||
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
|
||||
@ -43,7 +42,6 @@ _x = ExperimentalNamespace(
|
||||
asset=asset,
|
||||
client_state=ClientStateVar.create,
|
||||
hooks=hooks,
|
||||
vars=vars,
|
||||
layout=layout,
|
||||
progress=progress,
|
||||
PropsBase=PropsBase,
|
||||
|
@ -6,6 +6,7 @@ import dataclasses
|
||||
import functools
|
||||
import inspect
|
||||
import sys
|
||||
import traceback
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
@ -13,12 +14,14 @@ from typing import (
|
||||
Dict,
|
||||
Generic,
|
||||
List,
|
||||
Literal,
|
||||
Optional,
|
||||
Set,
|
||||
Tuple,
|
||||
Type,
|
||||
TypeVar,
|
||||
Union,
|
||||
get_args,
|
||||
overload,
|
||||
)
|
||||
|
||||
@ -26,7 +29,7 @@ from typing_extensions import ParamSpec, get_origin
|
||||
|
||||
from reflex import constants
|
||||
from reflex.base import Base
|
||||
from reflex.utils import serializers, types
|
||||
from reflex.utils import console, imports, serializers, types
|
||||
from reflex.utils.exceptions import VarTypeError
|
||||
from reflex.vars import (
|
||||
ImmutableVarData,
|
||||
@ -44,9 +47,16 @@ if TYPE_CHECKING:
|
||||
NumberVar,
|
||||
ToBooleanVarOperation,
|
||||
ToNumberVarOperation,
|
||||
EqualOperation,
|
||||
GreaterThanOperation,
|
||||
GreaterThanOrEqualOperation,
|
||||
LessThanOperation,
|
||||
LessThanOrEqualOperation,
|
||||
)
|
||||
from .object import ObjectVar, ToObjectOperation
|
||||
from .sequence import ArrayVar, StringVar, ToArrayOperation, ToStringOperation
|
||||
from reflex.state import BaseState
|
||||
|
||||
|
||||
VAR_TYPE = TypeVar("VAR_TYPE")
|
||||
|
||||
@ -376,10 +386,10 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
||||
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."
|
||||
)
|
||||
# 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(
|
||||
@ -405,6 +415,9 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
||||
|
||||
fixed_type = var_type if inspect.isclass(var_type) else get_origin(var_type)
|
||||
|
||||
if fixed_type is Union:
|
||||
return self
|
||||
|
||||
if issubclass(fixed_type, (int, float)):
|
||||
return self.to(NumberVar, var_type)
|
||||
if issubclass(fixed_type, dict):
|
||||
@ -417,6 +430,276 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
||||
return self.to(ObjectVar, var_type)
|
||||
return self
|
||||
|
||||
def get_default_value(self) -> Any:
|
||||
"""Get the default value of the var.
|
||||
|
||||
Returns:
|
||||
The default value of the var.
|
||||
|
||||
Raises:
|
||||
ImportError: If the var is a dataframe and pandas is not installed.
|
||||
"""
|
||||
if types.is_optional(self._var_type):
|
||||
return None
|
||||
|
||||
type_ = (
|
||||
get_origin(self._var_type)
|
||||
if types.is_generic_alias(self._var_type)
|
||||
else self._var_type
|
||||
)
|
||||
if type_ is Literal:
|
||||
args = get_args(self._var_type)
|
||||
return args[0] if args else None
|
||||
if issubclass(type_, str):
|
||||
return ""
|
||||
if issubclass(type_, types.get_args(Union[int, float])):
|
||||
return 0
|
||||
if issubclass(type_, bool):
|
||||
return False
|
||||
if issubclass(type_, list):
|
||||
return []
|
||||
if issubclass(type_, dict):
|
||||
return {}
|
||||
if issubclass(type_, tuple):
|
||||
return ()
|
||||
if types.is_dataframe(type_):
|
||||
try:
|
||||
import pandas as pd
|
||||
|
||||
return pd.DataFrame()
|
||||
except ImportError as e:
|
||||
raise ImportError(
|
||||
"Please install pandas to use dataframes in your app."
|
||||
) from e
|
||||
return set() if issubclass(type_, set) else None
|
||||
|
||||
def get_setter_name(self, include_state: bool = True) -> str:
|
||||
"""Get the name of the var's generated setter function.
|
||||
|
||||
Args:
|
||||
include_state: Whether to include the state name in the setter name.
|
||||
|
||||
Returns:
|
||||
The name of the setter function.
|
||||
"""
|
||||
setter = constants.SETTER_PREFIX + self._var_name
|
||||
if self._var_data is None:
|
||||
return setter
|
||||
if not include_state or self._var_data.state == "":
|
||||
return setter
|
||||
print("get_setter_name", self._var_data.state, setter)
|
||||
return ".".join((self._var_data.state, setter))
|
||||
|
||||
def get_setter(self) -> Callable[[BaseState, Any], None]:
|
||||
"""Get the var's setter function.
|
||||
|
||||
Returns:
|
||||
A function that that creates a setter for the var.
|
||||
"""
|
||||
|
||||
def setter(state: BaseState, value: Any):
|
||||
"""Get the setter for the var.
|
||||
|
||||
Args:
|
||||
state: The state within which we add the setter function.
|
||||
value: The value to set.
|
||||
"""
|
||||
if self._var_type in [int, float]:
|
||||
try:
|
||||
value = self._var_type(value)
|
||||
setattr(state, self._var_name, value)
|
||||
except ValueError:
|
||||
console.debug(
|
||||
f"{type(state).__name__}.{self._var_name}: Failed conversion of {value} to '{self._var_type.__name__}'. Value not set.",
|
||||
)
|
||||
else:
|
||||
setattr(state, self._var_name, value)
|
||||
|
||||
setter.__qualname__ = self.get_setter_name()
|
||||
|
||||
return setter
|
||||
|
||||
def __eq__(self, other: Var | Any) -> BooleanVar:
|
||||
"""
|
||||
Check if the current variable is equal to the given variable.
|
||||
|
||||
Args:
|
||||
other (Var | Any): The variable to compare with.
|
||||
|
||||
Returns:
|
||||
BooleanVar: A BooleanVar object representing the result of the equality check.
|
||||
"""
|
||||
from .number import EqualOperation
|
||||
|
||||
return EqualOperation(self, other)
|
||||
|
||||
def __ne__(self, other: Var | Any) -> BooleanVar:
|
||||
"""
|
||||
Check if the current object is not equal to the given object.
|
||||
|
||||
Parameters:
|
||||
other (Var | Any): The object to compare with.
|
||||
|
||||
Returns:
|
||||
BooleanVar: A BooleanVar object representing the result of the comparison.
|
||||
"""
|
||||
from .number import EqualOperation
|
||||
|
||||
return ~EqualOperation(self, other)
|
||||
|
||||
def __gt__(self, other: Var | Any) -> BooleanVar:
|
||||
"""
|
||||
Compare the current instance with another variable and return a BooleanVar representing the result of the greater than operation.
|
||||
|
||||
Args:
|
||||
other (Var | Any): The variable to compare with.
|
||||
|
||||
Returns:
|
||||
BooleanVar: A BooleanVar representing the result of the greater than operation.
|
||||
"""
|
||||
from .number import GreaterThanOperation
|
||||
|
||||
return GreaterThanOperation(self, other)
|
||||
|
||||
def __ge__(self, other: Var | Any) -> BooleanVar:
|
||||
"""
|
||||
Check if the value of this variable is greater than or equal to the value of another variable or object.
|
||||
|
||||
Args:
|
||||
other (Var | Any): The variable or object to compare with.
|
||||
|
||||
Returns:
|
||||
BooleanVar: A BooleanVar object representing the result of the comparison.
|
||||
"""
|
||||
from .number import GreaterThanOrEqualOperation
|
||||
|
||||
return GreaterThanOrEqualOperation(self, other)
|
||||
|
||||
def __lt__(self, other: Var | Any) -> BooleanVar:
|
||||
"""
|
||||
Compare the current instance with another variable using the less than (<) operator.
|
||||
|
||||
Args:
|
||||
other: The variable to compare with.
|
||||
|
||||
Returns:
|
||||
A `BooleanVar` object representing the result of the comparison.
|
||||
"""
|
||||
from .number import LessThanOperation
|
||||
|
||||
return LessThanOperation(self, other)
|
||||
|
||||
def __le__(self, other: Var | Any) -> BooleanVar:
|
||||
"""
|
||||
Compare if the current instance is less than or equal to the given value.
|
||||
|
||||
Args:
|
||||
other: The value to compare with.
|
||||
|
||||
Returns:
|
||||
A BooleanVar object representing the result of the comparison.
|
||||
"""
|
||||
from .number import LessThanOrEqualOperation
|
||||
|
||||
return LessThanOrEqualOperation(self, other)
|
||||
|
||||
def bool(self) -> BooleanVar:
|
||||
"""Convert the var to a boolean.
|
||||
|
||||
Returns:
|
||||
The boolean var.
|
||||
"""
|
||||
from .number import ToBooleanVarOperation
|
||||
|
||||
return ToBooleanVarOperation(self)
|
||||
|
||||
def __and__(self, other: Var | Any) -> ImmutableVar:
|
||||
"""Perform a logical AND operation on the current instance and another variable.
|
||||
|
||||
Args:
|
||||
other: The variable to perform the logical AND operation with.
|
||||
|
||||
Returns:
|
||||
A `BooleanVar` object representing the result of the logical AND operation.
|
||||
"""
|
||||
|
||||
return AndOperation(self, other)
|
||||
|
||||
def __rand__(self, other: Var | Any) -> ImmutableVar:
|
||||
"""Perform a logical AND operation on the current instance and another variable.
|
||||
|
||||
Args:
|
||||
other: The variable to perform the logical AND operation with.
|
||||
|
||||
Returns:
|
||||
A `BooleanVar` object representing the result of the logical AND operation.
|
||||
"""
|
||||
|
||||
return AndOperation(other, self)
|
||||
|
||||
def __or__(self, other: Var | Any) -> ImmutableVar:
|
||||
"""Perform a logical OR operation on the current instance and another variable.
|
||||
|
||||
Args:
|
||||
other: The variable to perform the logical OR operation with.
|
||||
|
||||
Returns:
|
||||
A `BooleanVar` object representing the result of the logical OR operation.
|
||||
"""
|
||||
|
||||
return OrOperation(self, other)
|
||||
|
||||
def __ror__(self, other: Var | Any) -> ImmutableVar:
|
||||
"""Perform a logical OR operation on the current instance and another variable.
|
||||
|
||||
Args:
|
||||
other: The variable to perform the logical OR operation with.
|
||||
|
||||
Returns:
|
||||
A `BooleanVar` object representing the result of the logical OR operation.
|
||||
"""
|
||||
|
||||
return OrOperation(other, self)
|
||||
|
||||
def __invert__(self) -> BooleanVar:
|
||||
"""Perform a logical NOT operation on the current instance.
|
||||
|
||||
Returns:
|
||||
A `BooleanVar` object representing the result of the logical NOT operation.
|
||||
"""
|
||||
from .number import BooleanNotOperation
|
||||
|
||||
return BooleanNotOperation(self.bool())
|
||||
|
||||
def to_string(self) -> ImmutableVar:
|
||||
"""Convert the var to a string.
|
||||
|
||||
Returns:
|
||||
The string var.
|
||||
"""
|
||||
from .function import JSON_STRINGIFY
|
||||
|
||||
return JSON_STRINGIFY.call(self)
|
||||
|
||||
def as_ref(self) -> ImmutableVar:
|
||||
"""Get a reference to the var.
|
||||
|
||||
Returns:
|
||||
The reference to the var.
|
||||
"""
|
||||
|
||||
from .object import ObjectVar
|
||||
|
||||
refs = ImmutableVar(
|
||||
_var_name="refs",
|
||||
_var_data=ImmutableVarData(
|
||||
imports={
|
||||
f"/{constants.Dirs.STATE_PATH}": [imports.ImportVar(tag="refs")]
|
||||
}
|
||||
),
|
||||
).to(ObjectVar)
|
||||
return refs[self]
|
||||
|
||||
|
||||
OUTPUT = TypeVar("OUTPUT", bound=ImmutableVar)
|
||||
|
||||
@ -457,6 +740,9 @@ class LiteralVar(ImmutableVar):
|
||||
value.dict(), _var_type=type(value), _var_data=_var_data
|
||||
)
|
||||
|
||||
if isinstance(value, dict):
|
||||
return LiteralObjectVar(value, _var_data=_var_data)
|
||||
|
||||
from .number import LiteralBooleanVar, LiteralNumberVar
|
||||
from .sequence import LiteralArrayVar, LiteralStringVar
|
||||
|
||||
@ -467,7 +753,6 @@ class LiteralVar(ImmutableVar):
|
||||
int: LiteralNumberVar,
|
||||
float: LiteralNumberVar,
|
||||
bool: LiteralBooleanVar,
|
||||
dict: LiteralObjectVar,
|
||||
list: LiteralArrayVar,
|
||||
tuple: LiteralArrayVar,
|
||||
set: LiteralArrayVar,
|
||||
@ -581,3 +866,165 @@ def figure_out_type(value: Any) -> Type:
|
||||
unionize(*(figure_out_type(v) for v in value.values())),
|
||||
]
|
||||
return type(value)
|
||||
|
||||
|
||||
@dataclasses.dataclass(
|
||||
eq=False,
|
||||
frozen=True,
|
||||
**{"slots": True} if sys.version_info >= (3, 10) else {},
|
||||
)
|
||||
class AndOperation(ImmutableVar):
|
||||
"""Class for the logical AND operation."""
|
||||
|
||||
# The first var.
|
||||
_var1: Var = dataclasses.field(default_factory=lambda: LiteralVar.create(None))
|
||||
|
||||
# The second var.
|
||||
_var2: Var = dataclasses.field(default_factory=lambda: LiteralVar.create(None))
|
||||
|
||||
def __init__(
|
||||
self, var1: Var | Any, var2: Var | Any, _var_data: VarData | None = None
|
||||
):
|
||||
"""Initialize the AndOperation.
|
||||
|
||||
Args:
|
||||
var1: The first var.
|
||||
var2: The second var.
|
||||
_var_data: Additional hooks and imports associated with the Var.
|
||||
"""
|
||||
super(type(self), self).__init__(
|
||||
_var_name="",
|
||||
_var_type=Union[var1._var_type, var2._var_type],
|
||||
_var_data=ImmutableVarData.merge(_var_data),
|
||||
)
|
||||
object.__setattr__(
|
||||
self, "_var1", var1 if isinstance(var1, Var) else LiteralVar.create(var1)
|
||||
)
|
||||
object.__setattr__(
|
||||
self, "_var2", var2 if isinstance(var2, Var) else LiteralVar.create(var2)
|
||||
)
|
||||
object.__delattr__(self, "_var_name")
|
||||
|
||||
@functools.cached_property
|
||||
def _cached_var_name(self) -> str:
|
||||
"""Get the cached var name.
|
||||
|
||||
Returns:
|
||||
The cached var name.
|
||||
"""
|
||||
return f"({str(self._var1)} && {str(self._var2)})"
|
||||
|
||||
def __getattr__(self, name: str) -> Any:
|
||||
"""Get an attribute of the var.
|
||||
|
||||
Args:
|
||||
name: The name of the attribute.
|
||||
|
||||
Returns:
|
||||
The attribute.
|
||||
"""
|
||||
if name == "_var_name":
|
||||
return self._cached_var_name
|
||||
return getattr(super(type(self), self), name)
|
||||
|
||||
@functools.cached_property
|
||||
def _cached_get_all_var_data(self) -> ImmutableVarData | None:
|
||||
"""Get the cached VarData.
|
||||
|
||||
Returns:
|
||||
The cached VarData.
|
||||
"""
|
||||
return ImmutableVarData.merge(
|
||||
self._var1._get_all_var_data(),
|
||||
self._var2._get_all_var_data(),
|
||||
self._var_data,
|
||||
)
|
||||
|
||||
def _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 self._cached_get_all_var_data
|
||||
|
||||
|
||||
@dataclasses.dataclass(
|
||||
eq=False,
|
||||
frozen=True,
|
||||
**{"slots": True} if sys.version_info >= (3, 10) else {},
|
||||
)
|
||||
class OrOperation(ImmutableVar):
|
||||
"""Class for the logical OR operation."""
|
||||
|
||||
# The first var.
|
||||
_var1: Var = dataclasses.field(default_factory=lambda: LiteralVar.create(None))
|
||||
|
||||
# The second var.
|
||||
_var2: Var = dataclasses.field(default_factory=lambda: LiteralVar.create(None))
|
||||
|
||||
def __init__(
|
||||
self, var1: Var | Any, var2: Var | Any, _var_data: VarData | None = None
|
||||
):
|
||||
"""Initialize the OrOperation.
|
||||
|
||||
Args:
|
||||
var1: The first var.
|
||||
var2: The second var.
|
||||
_var_data: Additional hooks and imports associated with the Var.
|
||||
"""
|
||||
super(type(self), self).__init__(
|
||||
_var_name="",
|
||||
_var_type=Union[var1._var_type, var2._var_type],
|
||||
_var_data=ImmutableVarData.merge(_var_data),
|
||||
)
|
||||
object.__setattr__(
|
||||
self, "_var1", var1 if isinstance(var1, Var) else LiteralVar.create(var1)
|
||||
)
|
||||
object.__setattr__(
|
||||
self, "_var2", var2 if isinstance(var2, Var) else LiteralVar.create(var2)
|
||||
)
|
||||
object.__delattr__(self, "_var_name")
|
||||
|
||||
@functools.cached_property
|
||||
def _cached_var_name(self) -> str:
|
||||
"""Get the cached var name.
|
||||
|
||||
Returns:
|
||||
The cached var name.
|
||||
"""
|
||||
return f"({str(self._var1)} || {str(self._var2)})"
|
||||
|
||||
def __getattr__(self, name: str) -> Any:
|
||||
"""Get an attribute of the var.
|
||||
|
||||
Args:
|
||||
name: The name of the attribute.
|
||||
|
||||
Returns:
|
||||
The attribute.
|
||||
"""
|
||||
if name == "_var_name":
|
||||
return self._cached_var_name
|
||||
return getattr(super(type(self), self), name)
|
||||
|
||||
@functools.cached_property
|
||||
def _cached_get_all_var_data(self) -> ImmutableVarData | None:
|
||||
"""Get the cached VarData.
|
||||
|
||||
Returns:
|
||||
The cached VarData.
|
||||
"""
|
||||
return ImmutableVarData.merge(
|
||||
self._var1._get_all_var_data(),
|
||||
self._var2._get_all_var_data(),
|
||||
self._var_data,
|
||||
)
|
||||
|
||||
def _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 self._cached_get_all_var_data
|
@ -7,7 +7,7 @@ import sys
|
||||
from functools import cached_property
|
||||
from typing import Any, Callable, Optional, Tuple, Type, Union
|
||||
|
||||
from reflex.experimental.vars.base import ImmutableVar, LiteralVar
|
||||
from .base import ImmutableVar, LiteralVar
|
||||
from reflex.vars import ImmutableVarData, Var, VarData
|
||||
|
||||
|
||||
@ -288,3 +288,6 @@ class ToFunctionOperation(FunctionVar):
|
||||
The VarData of the components and all of its children.
|
||||
"""
|
||||
return self._cached_get_all_var_data
|
||||
|
||||
|
||||
JSON_STRINGIFY = FunctionStringVar("JSON.stringify")
|
@ -8,7 +8,9 @@ import sys
|
||||
from functools import cached_property
|
||||
from typing import Any, Union
|
||||
|
||||
from reflex.experimental.vars.base import (
|
||||
from reflex.utils.types import GenericType
|
||||
|
||||
from .base import (
|
||||
ImmutableVar,
|
||||
LiteralVar,
|
||||
)
|
||||
@ -188,54 +190,6 @@ class NumberVar(ImmutableVar[Union[int, float]]):
|
||||
"""
|
||||
return NumberNegateOperation(self)
|
||||
|
||||
def __and__(self, other: number_types | boolean_types) -> BooleanAndOperation:
|
||||
"""Boolean AND two numbers.
|
||||
|
||||
Args:
|
||||
other: The other number.
|
||||
|
||||
Returns:
|
||||
The boolean AND operation.
|
||||
"""
|
||||
boolified_other = other.bool() if isinstance(other, Var) else bool(other)
|
||||
return BooleanAndOperation(self.bool(), boolified_other)
|
||||
|
||||
def __rand__(self, other: number_types | boolean_types) -> BooleanAndOperation:
|
||||
"""Boolean AND two numbers.
|
||||
|
||||
Args:
|
||||
other: The other number.
|
||||
|
||||
Returns:
|
||||
The boolean AND operation.
|
||||
"""
|
||||
boolified_other = other.bool() if isinstance(other, Var) else bool(other)
|
||||
return BooleanAndOperation(boolified_other, self.bool())
|
||||
|
||||
def __or__(self, other: number_types | boolean_types) -> BooleanOrOperation:
|
||||
"""Boolean OR two numbers.
|
||||
|
||||
Args:
|
||||
other: The other number.
|
||||
|
||||
Returns:
|
||||
The boolean OR operation.
|
||||
"""
|
||||
boolified_other = other.bool() if isinstance(other, Var) else bool(other)
|
||||
return BooleanOrOperation(self.bool(), boolified_other)
|
||||
|
||||
def __ror__(self, other: number_types | boolean_types) -> BooleanOrOperation:
|
||||
"""Boolean OR two numbers.
|
||||
|
||||
Args:
|
||||
other: The other number.
|
||||
|
||||
Returns:
|
||||
The boolean OR operation.
|
||||
"""
|
||||
boolified_other = other.bool() if isinstance(other, Var) else bool(other)
|
||||
return BooleanOrOperation(boolified_other, self.bool())
|
||||
|
||||
def __invert__(self) -> BooleanNotOperation:
|
||||
"""Boolean NOT the number.
|
||||
|
||||
@ -284,7 +238,7 @@ class NumberVar(ImmutableVar[Union[int, float]]):
|
||||
"""
|
||||
return NumberTruncOperation(self)
|
||||
|
||||
def __lt__(self, other: number_types | boolean_types) -> LessThanOperation:
|
||||
def __lt__(self, other: Any) -> LessThanOperation:
|
||||
"""Less than comparison.
|
||||
|
||||
Args:
|
||||
@ -293,9 +247,11 @@ class NumberVar(ImmutableVar[Union[int, float]]):
|
||||
Returns:
|
||||
The result of the comparison.
|
||||
"""
|
||||
return LessThanOperation(self, +other)
|
||||
if isinstance(other, (NumberVar, BooleanVar, int, float, bool)):
|
||||
return LessThanOperation(self, +other)
|
||||
return LessThanOperation(self, other)
|
||||
|
||||
def __le__(self, other: number_types | boolean_types) -> LessThanOrEqualOperation:
|
||||
def __le__(self, other: Any) -> LessThanOrEqualOperation:
|
||||
"""Less than or equal comparison.
|
||||
|
||||
Args:
|
||||
@ -304,9 +260,11 @@ class NumberVar(ImmutableVar[Union[int, float]]):
|
||||
Returns:
|
||||
The result of the comparison.
|
||||
"""
|
||||
return LessThanOrEqualOperation(self, +other)
|
||||
if isinstance(other, (NumberVar, BooleanVar, int, float, bool)):
|
||||
return LessThanOrEqualOperation(self, +other)
|
||||
return LessThanOrEqualOperation(self, other)
|
||||
|
||||
def __eq__(self, other: number_types | boolean_types) -> EqualOperation:
|
||||
def __eq__(self, other: Any) -> EqualOperation:
|
||||
"""Equal comparison.
|
||||
|
||||
Args:
|
||||
@ -315,9 +273,11 @@ class NumberVar(ImmutableVar[Union[int, float]]):
|
||||
Returns:
|
||||
The result of the comparison.
|
||||
"""
|
||||
return EqualOperation(self, +other)
|
||||
if isinstance(other, (NumberVar, BooleanVar, int, float, bool)):
|
||||
return EqualOperation(self, +other)
|
||||
return EqualOperation(self, other)
|
||||
|
||||
def __ne__(self, other: number_types | boolean_types) -> NotEqualOperation:
|
||||
def __ne__(self, other: Any) -> NotEqualOperation:
|
||||
"""Not equal comparison.
|
||||
|
||||
Args:
|
||||
@ -326,9 +286,11 @@ class NumberVar(ImmutableVar[Union[int, float]]):
|
||||
Returns:
|
||||
The result of the comparison.
|
||||
"""
|
||||
return NotEqualOperation(self, +other)
|
||||
if isinstance(other, (NumberVar, BooleanVar, int, float, bool)):
|
||||
return NotEqualOperation(self, +other)
|
||||
return NotEqualOperation(self, other)
|
||||
|
||||
def __gt__(self, other: number_types | boolean_types) -> GreaterThanOperation:
|
||||
def __gt__(self, other: Any) -> GreaterThanOperation:
|
||||
"""Greater than comparison.
|
||||
|
||||
Args:
|
||||
@ -337,11 +299,11 @@ class NumberVar(ImmutableVar[Union[int, float]]):
|
||||
Returns:
|
||||
The result of the comparison.
|
||||
"""
|
||||
return GreaterThanOperation(self, +other)
|
||||
if isinstance(other, (NumberVar, BooleanVar, int, float, bool)):
|
||||
return GreaterThanOperation(self, +other)
|
||||
return GreaterThanOperation(self, other)
|
||||
|
||||
def __ge__(
|
||||
self, other: number_types | boolean_types
|
||||
) -> GreaterThanOrEqualOperation:
|
||||
def __ge__(self, other: Any) -> GreaterThanOrEqualOperation:
|
||||
"""Greater than or equal comparison.
|
||||
|
||||
Args:
|
||||
@ -350,7 +312,9 @@ class NumberVar(ImmutableVar[Union[int, float]]):
|
||||
Returns:
|
||||
The result of the comparison.
|
||||
"""
|
||||
return GreaterThanOrEqualOperation(self, +other)
|
||||
if isinstance(other, (NumberVar, BooleanVar, int, float, bool)):
|
||||
return GreaterThanOrEqualOperation(self, +other)
|
||||
return GreaterThanOrEqualOperation(self, other)
|
||||
|
||||
def bool(self) -> NotEqualOperation:
|
||||
"""Boolean conversion.
|
||||
@ -696,50 +660,6 @@ class NumberTruncOperation(UnaryNumberOperation):
|
||||
class BooleanVar(ImmutableVar[bool]):
|
||||
"""Base class for immutable boolean vars."""
|
||||
|
||||
def __and__(self, other: bool) -> BooleanAndOperation:
|
||||
"""AND two booleans.
|
||||
|
||||
Args:
|
||||
other: The other boolean.
|
||||
|
||||
Returns:
|
||||
The boolean AND operation.
|
||||
"""
|
||||
return BooleanAndOperation(self, other)
|
||||
|
||||
def __rand__(self, other: bool) -> BooleanAndOperation:
|
||||
"""AND two booleans.
|
||||
|
||||
Args:
|
||||
other: The other boolean.
|
||||
|
||||
Returns:
|
||||
The boolean AND operation.
|
||||
"""
|
||||
return BooleanAndOperation(other, self)
|
||||
|
||||
def __or__(self, other: bool) -> BooleanOrOperation:
|
||||
"""OR two booleans.
|
||||
|
||||
Args:
|
||||
other: The other boolean.
|
||||
|
||||
Returns:
|
||||
The boolean OR operation.
|
||||
"""
|
||||
return BooleanOrOperation(self, other)
|
||||
|
||||
def __ror__(self, other: bool) -> BooleanOrOperation:
|
||||
"""OR two booleans.
|
||||
|
||||
Args:
|
||||
other: The other boolean.
|
||||
|
||||
Returns:
|
||||
The boolean OR operation.
|
||||
"""
|
||||
return BooleanOrOperation(other, self)
|
||||
|
||||
def __invert__(self) -> BooleanNotOperation:
|
||||
"""NOT the boolean.
|
||||
|
||||
@ -913,16 +833,16 @@ class BooleanToIntOperation(NumberVar):
|
||||
frozen=True,
|
||||
**{"slots": True} if sys.version_info >= (3, 10) else {},
|
||||
)
|
||||
class NumberComparisonOperation(BooleanVar):
|
||||
class ComparisonOperation(BooleanVar):
|
||||
"""Base class for immutable boolean vars that are the result of a comparison operation."""
|
||||
|
||||
a: number_types = dataclasses.field(default=0)
|
||||
b: number_types = dataclasses.field(default=0)
|
||||
a: Var = dataclasses.field(default_factory=lambda: LiteralBooleanVar(True))
|
||||
b: Var = dataclasses.field(default_factory=lambda: LiteralBooleanVar(True))
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
a: number_types,
|
||||
b: number_types,
|
||||
a: Var | Any,
|
||||
b: Var | Any,
|
||||
_var_data: VarData | None = None,
|
||||
):
|
||||
"""Initialize the comparison operation var.
|
||||
@ -932,13 +852,13 @@ class NumberComparisonOperation(BooleanVar):
|
||||
b: The second value.
|
||||
_var_data: Additional hooks and imports associated with the Var.
|
||||
"""
|
||||
super(NumberComparisonOperation, self).__init__(
|
||||
super(ComparisonOperation, self).__init__(
|
||||
_var_name="",
|
||||
_var_type=bool,
|
||||
_var_data=ImmutableVarData.merge(_var_data),
|
||||
)
|
||||
object.__setattr__(self, "a", a)
|
||||
object.__setattr__(self, "b", b)
|
||||
object.__setattr__(self, "a", a if isinstance(a, Var) else LiteralVar.create(a))
|
||||
object.__setattr__(self, "b", b if isinstance(b, Var) else LiteralVar.create(b))
|
||||
object.__delattr__(self, "_var_name")
|
||||
|
||||
@cached_property
|
||||
@ -961,7 +881,7 @@ class NumberComparisonOperation(BooleanVar):
|
||||
"""
|
||||
if name == "_var_name":
|
||||
return self._cached_var_name
|
||||
getattr(super(NumberComparisonOperation, self), name)
|
||||
getattr(super(ComparisonOperation, self), name)
|
||||
|
||||
@cached_property
|
||||
def _cached_get_all_var_data(self) -> ImmutableVarData | None:
|
||||
@ -980,7 +900,7 @@ class NumberComparisonOperation(BooleanVar):
|
||||
return self._cached_get_all_var_data
|
||||
|
||||
|
||||
class GreaterThanOperation(NumberComparisonOperation):
|
||||
class GreaterThanOperation(ComparisonOperation):
|
||||
"""Base class for immutable boolean vars that are the result of a greater than operation."""
|
||||
|
||||
@cached_property
|
||||
@ -995,7 +915,7 @@ class GreaterThanOperation(NumberComparisonOperation):
|
||||
return f"({str(first_value)} > {str(second_value)})"
|
||||
|
||||
|
||||
class GreaterThanOrEqualOperation(NumberComparisonOperation):
|
||||
class GreaterThanOrEqualOperation(ComparisonOperation):
|
||||
"""Base class for immutable boolean vars that are the result of a greater than or equal operation."""
|
||||
|
||||
@cached_property
|
||||
@ -1010,7 +930,7 @@ class GreaterThanOrEqualOperation(NumberComparisonOperation):
|
||||
return f"({str(first_value)} >= {str(second_value)})"
|
||||
|
||||
|
||||
class LessThanOperation(NumberComparisonOperation):
|
||||
class LessThanOperation(ComparisonOperation):
|
||||
"""Base class for immutable boolean vars that are the result of a less than operation."""
|
||||
|
||||
@cached_property
|
||||
@ -1025,7 +945,7 @@ class LessThanOperation(NumberComparisonOperation):
|
||||
return f"({str(first_value)} < {str(second_value)})"
|
||||
|
||||
|
||||
class LessThanOrEqualOperation(NumberComparisonOperation):
|
||||
class LessThanOrEqualOperation(ComparisonOperation):
|
||||
"""Base class for immutable boolean vars that are the result of a less than or equal operation."""
|
||||
|
||||
@cached_property
|
||||
@ -1040,7 +960,7 @@ class LessThanOrEqualOperation(NumberComparisonOperation):
|
||||
return f"({str(first_value)} <= {str(second_value)})"
|
||||
|
||||
|
||||
class EqualOperation(NumberComparisonOperation):
|
||||
class EqualOperation(ComparisonOperation):
|
||||
"""Base class for immutable boolean vars that are the result of an equal operation."""
|
||||
|
||||
@cached_property
|
||||
@ -1052,10 +972,10 @@ class EqualOperation(NumberComparisonOperation):
|
||||
"""
|
||||
first_value = self.a if isinstance(self.a, Var) else LiteralVar.create(self.a)
|
||||
second_value = self.b if isinstance(self.b, Var) else LiteralVar.create(self.b)
|
||||
return f"({str(first_value)} == {str(second_value)})"
|
||||
return f"({str(first_value)} === {str(second_value)})"
|
||||
|
||||
|
||||
class NotEqualOperation(NumberComparisonOperation):
|
||||
class NotEqualOperation(ComparisonOperation):
|
||||
"""Base class for immutable boolean vars that are the result of a not equal operation."""
|
||||
|
||||
@cached_property
|
||||
@ -1139,36 +1059,6 @@ class LogicalOperation(BooleanVar):
|
||||
return self._cached_get_all_var_data
|
||||
|
||||
|
||||
class BooleanAndOperation(LogicalOperation):
|
||||
"""Base class for immutable boolean vars that are the result of a logical AND operation."""
|
||||
|
||||
@cached_property
|
||||
def _cached_var_name(self) -> str:
|
||||
"""The name of the var.
|
||||
|
||||
Returns:
|
||||
The name of the var.
|
||||
"""
|
||||
first_value = self.a if isinstance(self.a, Var) else LiteralVar.create(self.a)
|
||||
second_value = self.b if isinstance(self.b, Var) else LiteralVar.create(self.b)
|
||||
return f"({str(first_value)} && {str(second_value)})"
|
||||
|
||||
|
||||
class BooleanOrOperation(LogicalOperation):
|
||||
"""Base class for immutable boolean vars that are the result of a logical OR operation."""
|
||||
|
||||
@cached_property
|
||||
def _cached_var_name(self) -> str:
|
||||
"""The name of the var.
|
||||
|
||||
Returns:
|
||||
The name of the var.
|
||||
"""
|
||||
first_value = self.a if isinstance(self.a, Var) else LiteralVar.create(self.a)
|
||||
second_value = self.b if isinstance(self.b, Var) else LiteralVar.create(self.b)
|
||||
return f"({str(first_value)} || {str(second_value)})"
|
||||
|
||||
|
||||
class BooleanNotOperation(BooleanVar):
|
||||
"""Base class for immutable boolean vars that are the result of a logical NOT operation."""
|
||||
|
||||
@ -1428,7 +1318,7 @@ class ToBooleanVarOperation(BooleanVar):
|
||||
Returns:
|
||||
The name of the var.
|
||||
"""
|
||||
return str(self._original_value)
|
||||
return f"Boolean({str(self._original_value)})"
|
||||
|
||||
def __getattr__(self, name: str) -> Any:
|
||||
"""Get an attribute of the var.
|
||||
@ -1456,3 +1346,84 @@ class ToBooleanVarOperation(BooleanVar):
|
||||
|
||||
def _get_all_var_data(self) -> ImmutableVarData | None:
|
||||
return self._cached_get_all_var_data
|
||||
|
||||
|
||||
class TernaryOperator(ImmutableVar):
|
||||
"""Base class for immutable vars that are the result of a ternary operation."""
|
||||
|
||||
condition: Var = dataclasses.field(default_factory=lambda: LiteralBooleanVar(False))
|
||||
if_true: Var = dataclasses.field(default_factory=lambda: LiteralNumberVar(0))
|
||||
if_false: Var = dataclasses.field(default_factory=lambda: LiteralNumberVar(0))
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
condition: Var | Any,
|
||||
if_true: Var | Any,
|
||||
if_false: Var | Any,
|
||||
_var_type: GenericType | None = None,
|
||||
_var_data: VarData | None = None,
|
||||
):
|
||||
"""Initialize the ternary operation var.
|
||||
|
||||
Args:
|
||||
condition: The condition.
|
||||
if_true: The value if the condition is true.
|
||||
if_false: The value if the condition is false.
|
||||
_var_data: Additional hooks and imports associated with the Var.
|
||||
"""
|
||||
condition = (
|
||||
condition if isinstance(condition, Var) else LiteralVar.create(condition)
|
||||
)
|
||||
if_true = if_true if isinstance(if_true, Var) else LiteralVar.create(if_true)
|
||||
if_false = (
|
||||
if_false if isinstance(if_false, Var) else LiteralVar.create(if_false)
|
||||
)
|
||||
|
||||
super(TernaryOperator, self).__init__(
|
||||
_var_name="",
|
||||
_var_type=_var_type or Union[if_true._var_type, if_false._var_type],
|
||||
_var_data=ImmutableVarData.merge(_var_data),
|
||||
)
|
||||
object.__setattr__(self, "condition", condition)
|
||||
object.__setattr__(self, "if_true", if_true)
|
||||
object.__setattr__(self, "if_false", if_false)
|
||||
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 f"({str(self.condition)} ? {str(self.if_true)} : {str(self.if_false)})"
|
||||
|
||||
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(TernaryOperator, 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.condition._get_all_var_data(),
|
||||
self.if_true._get_all_var_data(),
|
||||
self.if_false._get_all_var_data(),
|
||||
self._var_data,
|
||||
)
|
||||
|
||||
def _get_all_var_data(self) -> ImmutableVarData | None:
|
||||
return self._cached_get_all_var_data
|
@ -22,13 +22,15 @@ from typing import (
|
||||
|
||||
from typing_extensions import get_origin
|
||||
|
||||
from reflex.experimental.vars.base import (
|
||||
from reflex.utils import console
|
||||
|
||||
from .base import (
|
||||
ImmutableVar,
|
||||
LiteralVar,
|
||||
figure_out_type,
|
||||
)
|
||||
from reflex.experimental.vars.number import NumberVar
|
||||
from reflex.experimental.vars.sequence import ArrayVar, StringVar
|
||||
from .number import BooleanVar, NumberVar
|
||||
from .sequence import ArrayVar, StringVar
|
||||
from reflex.utils.exceptions import VarAttributeError
|
||||
from reflex.utils.types import GenericType, get_attribute_access_type
|
||||
from reflex.vars import ImmutableVarData, Var, VarData
|
||||
@ -46,23 +48,13 @@ OTHER_KEY_TYPE = TypeVar("OTHER_KEY_TYPE")
|
||||
class ObjectVar(ImmutableVar[OBJECT_TYPE]):
|
||||
"""Base class for immutable object vars."""
|
||||
|
||||
@overload
|
||||
def _key_type(self: ObjectVar[Dict[KEY_TYPE, VALUE_TYPE]]) -> KEY_TYPE: ...
|
||||
|
||||
@overload
|
||||
def _key_type(self) -> Type: ...
|
||||
|
||||
def _key_type(self) -> Type:
|
||||
"""Get the type of the keys of the object.
|
||||
|
||||
Returns:
|
||||
The type of the keys of the object.
|
||||
"""
|
||||
fixed_type = (
|
||||
self._var_type if isclass(self._var_type) else get_origin(self._var_type)
|
||||
)
|
||||
args = get_args(self._var_type) if issubclass(fixed_type, dict) else ()
|
||||
return args[0] if args else Any
|
||||
return str
|
||||
|
||||
@overload
|
||||
def _value_type(self: ObjectVar[Dict[KEY_TYPE, VALUE_TYPE]]) -> VALUE_TYPE: ...
|
||||
@ -82,15 +74,7 @@ class ObjectVar(ImmutableVar[OBJECT_TYPE]):
|
||||
args = get_args(self._var_type) if issubclass(fixed_type, dict) else ()
|
||||
return args[1] if args else Any
|
||||
|
||||
@overload
|
||||
def keys(
|
||||
self: ObjectVar[Dict[KEY_TYPE, VALUE_TYPE]],
|
||||
) -> ArrayVar[List[KEY_TYPE]]: ...
|
||||
|
||||
@overload
|
||||
def keys(self) -> ArrayVar: ...
|
||||
|
||||
def keys(self) -> ArrayVar:
|
||||
def keys(self) -> ArrayVar[List[str]]:
|
||||
"""Get the keys of the object.
|
||||
|
||||
Returns:
|
||||
@ -117,7 +101,7 @@ class ObjectVar(ImmutableVar[OBJECT_TYPE]):
|
||||
@overload
|
||||
def entries(
|
||||
self: ObjectVar[Dict[KEY_TYPE, VALUE_TYPE]],
|
||||
) -> ArrayVar[List[Tuple[KEY_TYPE, VALUE_TYPE]]]: ...
|
||||
) -> ArrayVar[List[Tuple[str, VALUE_TYPE]]]: ...
|
||||
|
||||
@overload
|
||||
def entries(self) -> ArrayVar: ...
|
||||
@ -258,6 +242,8 @@ class ObjectVar(ImmutableVar[OBJECT_TYPE]):
|
||||
Returns:
|
||||
The attribute of the var.
|
||||
"""
|
||||
if name.startswith("__") and name.endswith("__"):
|
||||
return getattr(super(type(self), self), name)
|
||||
fixed_type = (
|
||||
self._var_type if isclass(self._var_type) else get_origin(self._var_type)
|
||||
)
|
||||
@ -272,6 +258,17 @@ class ObjectVar(ImmutableVar[OBJECT_TYPE]):
|
||||
else:
|
||||
return ObjectItemOperation(self, name).guess_type()
|
||||
|
||||
def contains(self, key: Var | Any) -> BooleanVar:
|
||||
"""Check if the object contains a key.
|
||||
|
||||
Args:
|
||||
key: The key to check.
|
||||
|
||||
Returns:
|
||||
The result of the check.
|
||||
"""
|
||||
return ObjectHasOwnProperty(self, key)
|
||||
|
||||
|
||||
@dataclasses.dataclass(
|
||||
eq=False,
|
||||
@ -369,12 +366,12 @@ class LiteralObjectVar(LiteralVar, ObjectVar[OBJECT_TYPE]):
|
||||
return ImmutableVarData.merge(
|
||||
*[
|
||||
value._get_all_var_data()
|
||||
for key, value in self._var_value
|
||||
for value in self._var_value.values()
|
||||
if isinstance(value, Var)
|
||||
],
|
||||
*[
|
||||
key._get_all_var_data()
|
||||
for key, value in self._var_value
|
||||
for key in self._var_value.keys()
|
||||
if isinstance(key, Var)
|
||||
],
|
||||
self._var_data,
|
||||
@ -802,3 +799,81 @@ class ToObjectOperation(ObjectVar):
|
||||
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 ObjectHasOwnProperty(BooleanVar):
|
||||
"""Operation to check if an object has a property."""
|
||||
|
||||
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 has own property operation.
|
||||
|
||||
Args:
|
||||
value: The value of the operation.
|
||||
key: The key to check.
|
||||
_var_data: Additional hooks and imports associated with the operation.
|
||||
"""
|
||||
super(ObjectHasOwnProperty, self).__init__(
|
||||
_var_name="",
|
||||
_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)}.hasOwnProperty({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
|
@ -27,13 +27,13 @@ from typing_extensions import get_origin
|
||||
|
||||
from reflex import constants
|
||||
from reflex.constants.base import REFLEX_VAR_OPENING_TAG
|
||||
from reflex.experimental.vars.base import (
|
||||
from .base import (
|
||||
ImmutableVar,
|
||||
LiteralVar,
|
||||
figure_out_type,
|
||||
unionize,
|
||||
)
|
||||
from reflex.experimental.vars.number import (
|
||||
from .number import (
|
||||
BooleanVar,
|
||||
LiteralNumberVar,
|
||||
NotEqualOperation,
|
||||
@ -71,27 +71,29 @@ class StringVar(ImmutableVar[str]):
|
||||
"""
|
||||
return ConcatVarOperation(other, self)
|
||||
|
||||
def __mul__(self, other: int) -> ConcatVarOperation:
|
||||
"""Concatenate two strings.
|
||||
def __mul__(self, other: NumberVar | int) -> StringVar:
|
||||
"""
|
||||
Multiply the sequence by a number or an integer.
|
||||
|
||||
Args:
|
||||
other: The other string.
|
||||
other (NumberVar | int): The number or integer to multiply the sequence by.
|
||||
|
||||
Returns:
|
||||
The string concatenation operation.
|
||||
StringVar: The resulting sequence after multiplication.
|
||||
"""
|
||||
return ConcatVarOperation(*[self for _ in range(other)])
|
||||
return (self.split() * other).join()
|
||||
|
||||
def __rmul__(self, other: int) -> ConcatVarOperation:
|
||||
"""Concatenate two strings.
|
||||
def __rmul__(self, other: NumberVar | int) -> StringVar:
|
||||
"""
|
||||
Multiply the sequence by a number or an integer.
|
||||
|
||||
Args:
|
||||
other: The other string.
|
||||
other (NumberVar | int): The number or integer to multiply the sequence by.
|
||||
|
||||
Returns:
|
||||
The string concatenation operation.
|
||||
StringVar: The resulting sequence after multiplication.
|
||||
"""
|
||||
return ConcatVarOperation(*[self for _ in range(other)])
|
||||
return (self.split() * other).join()
|
||||
|
||||
@overload
|
||||
def __getitem__(self, i: slice) -> ArrayJoinOperation: ...
|
||||
@ -596,10 +598,17 @@ class LiteralStringVar(LiteralVar, StringVar):
|
||||
var_data.interpolations = [
|
||||
(realstart, realstart + string_length)
|
||||
]
|
||||
var_content = value[end : (end + string_length)]
|
||||
if (
|
||||
var_content[0] == "{"
|
||||
and var_content[-1] == "}"
|
||||
and strings_and_vals
|
||||
and strings_and_vals[-1][-1] == "$"
|
||||
):
|
||||
strings_and_vals[-1] = strings_and_vals[-1][:-1]
|
||||
var_content = "(" + var_content[1:-1] + ")"
|
||||
strings_and_vals.append(
|
||||
ImmutableVar.create_safe(
|
||||
value[end : (end + string_length)], _var_data=var_data
|
||||
)
|
||||
ImmutableVar.create_safe(var_content, _var_data=var_data)
|
||||
)
|
||||
value = value[(end + string_length) :]
|
||||
|
||||
@ -728,8 +737,6 @@ VALUE_TYPE = TypeVar("VALUE_TYPE")
|
||||
class ArrayVar(ImmutableVar[ARRAY_VAR_TYPE]):
|
||||
"""Base class for immutable array vars."""
|
||||
|
||||
from reflex.experimental.vars.sequence import StringVar
|
||||
|
||||
def join(self, sep: StringVar | str = "") -> ArrayJoinOperation:
|
||||
"""Join the elements of the array.
|
||||
|
||||
@ -739,7 +746,6 @@ class ArrayVar(ImmutableVar[ARRAY_VAR_TYPE]):
|
||||
Returns:
|
||||
The joined elements.
|
||||
"""
|
||||
from reflex.experimental.vars.sequence import ArrayJoinOperation
|
||||
|
||||
return ArrayJoinOperation(self, sep)
|
||||
|
||||
@ -751,6 +757,18 @@ class ArrayVar(ImmutableVar[ARRAY_VAR_TYPE]):
|
||||
"""
|
||||
return ArrayReverseOperation(self)
|
||||
|
||||
def __add__(self, other: ArrayVar[ARRAY_VAR_TYPE]) -> ArrayConcatOperation:
|
||||
"""
|
||||
Concatenate two arrays.
|
||||
|
||||
Parameters:
|
||||
other (ArrayVar[ARRAY_VAR_TYPE]): The other array to concatenate.
|
||||
|
||||
Returns:
|
||||
ArrayConcatOperation: The concatenation of the two arrays.
|
||||
"""
|
||||
return ArrayConcatOperation(self, other)
|
||||
|
||||
@overload
|
||||
def __getitem__(self, i: slice) -> ArrayVar[ARRAY_VAR_TYPE]: ...
|
||||
|
||||
@ -915,6 +933,30 @@ class ArrayVar(ImmutableVar[ARRAY_VAR_TYPE]):
|
||||
"""
|
||||
return ArrayContainsOperation(self, other)
|
||||
|
||||
def __mul__(self, other: NumberVar | int) -> ArrayVar[ARRAY_VAR_TYPE]:
|
||||
"""
|
||||
Multiply the sequence by a number or integer.
|
||||
|
||||
Parameters:
|
||||
other (NumberVar | int): The number or integer to multiply the sequence by.
|
||||
|
||||
Returns:
|
||||
ArrayVar[ARRAY_VAR_TYPE]: The result of multiplying the sequence by the given number or integer.
|
||||
"""
|
||||
return ArrayRepeatOperation(self, other)
|
||||
|
||||
def __rmul__(self, other: NumberVar | int) -> ArrayVar[ARRAY_VAR_TYPE]:
|
||||
"""
|
||||
Multiply the sequence by a number or integer.
|
||||
|
||||
Parameters:
|
||||
other (NumberVar | int): The number or integer to multiply the sequence by.
|
||||
|
||||
Returns:
|
||||
ArrayVar[ARRAY_VAR_TYPE]: The result of multiplying the sequence by the given number or integer.
|
||||
"""
|
||||
return ArrayRepeatOperation(self, other)
|
||||
|
||||
|
||||
LIST_ELEMENT = TypeVar("LIST_ELEMENT")
|
||||
|
||||
@ -1296,7 +1338,7 @@ class ArrayReverseOperation(ArrayToArrayOperation):
|
||||
Returns:
|
||||
The name of the var.
|
||||
"""
|
||||
return f"{str(self.a)}.reverse()"
|
||||
return f"{str(self.a)}.slice().reverse()"
|
||||
|
||||
|
||||
@dataclasses.dataclass(
|
||||
@ -1762,3 +1804,140 @@ class ToArrayOperation(ArrayVar):
|
||||
|
||||
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 ArrayRepeatOperation(ArrayVar):
|
||||
"""Base class for immutable array vars that are the result of an array repeat operation."""
|
||||
|
||||
a: ArrayVar = dataclasses.field(default_factory=lambda: LiteralArrayVar([]))
|
||||
n: NumberVar = dataclasses.field(default_factory=lambda: LiteralNumberVar(0))
|
||||
|
||||
def __init__(
|
||||
self, a: ArrayVar, n: NumberVar | int, _var_data: VarData | None = None
|
||||
):
|
||||
"""Initialize the array repeat operation var.
|
||||
|
||||
Args:
|
||||
a: The array.
|
||||
n: The number of times to repeat the array.
|
||||
_var_data: Additional hooks and imports associated with the Var.
|
||||
"""
|
||||
super(ArrayRepeatOperation, self).__init__(
|
||||
_var_name="",
|
||||
_var_type=a._var_type,
|
||||
_var_data=ImmutableVarData.merge(_var_data),
|
||||
)
|
||||
object.__setattr__(self, "a", a)
|
||||
object.__setattr__(
|
||||
self,
|
||||
"n",
|
||||
n if isinstance(n, Var) else LiteralNumberVar(n),
|
||||
)
|
||||
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 f"Array.from({{ length: {str(self.n)} }}).flatMap(() => {str(self.a)})"
|
||||
|
||||
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(ArrayRepeatOperation, 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.a._get_all_var_data(), self.n._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 ArrayConcatOperation(ArrayVar):
|
||||
"""Base class for immutable array vars that are the result of an array concat operation."""
|
||||
|
||||
a: ArrayVar = dataclasses.field(default_factory=lambda: LiteralArrayVar([]))
|
||||
b: ArrayVar = dataclasses.field(default_factory=lambda: LiteralArrayVar([]))
|
||||
|
||||
def __init__(self, a: ArrayVar, b: ArrayVar, _var_data: VarData | None = None):
|
||||
"""Initialize the array concat operation var.
|
||||
|
||||
Args:
|
||||
a: The first array.
|
||||
b: The second array.
|
||||
_var_data: Additional hooks and imports associated with the Var.
|
||||
"""
|
||||
# TODO: Figure out how to merge the types of a and b
|
||||
super(ArrayConcatOperation, self).__init__(
|
||||
_var_name="",
|
||||
_var_type=List[ARRAY_VAR_TYPE],
|
||||
_var_data=ImmutableVarData.merge(_var_data),
|
||||
)
|
||||
object.__setattr__(self, "a", a)
|
||||
object.__setattr__(self, "b", b)
|
||||
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 f"[...{str(self.a)}, ...{str(self.b)}]"
|
||||
|
||||
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(ArrayConcatOperation, 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.a._get_all_var_data(), self.b._get_all_var_data(), self._var_data
|
||||
)
|
||||
|
||||
def _get_all_var_data(self) -> ImmutableVarData | None:
|
||||
return self._cached_get_all_var_data
|
@ -32,6 +32,7 @@ import dill
|
||||
from sqlalchemy.orm import DeclarativeBase
|
||||
|
||||
from reflex.config import get_config
|
||||
from reflex.ivars.base import ImmutableVar
|
||||
|
||||
try:
|
||||
import pydantic.v1 as pydantic
|
||||
@ -55,7 +56,12 @@ from reflex.utils import console, format, prerequisites, types
|
||||
from reflex.utils.exceptions import ImmutableStateError, LockExpiredError
|
||||
from reflex.utils.exec import is_testing_env
|
||||
from reflex.utils.serializers import SerializedType, serialize, serializer
|
||||
from reflex.vars import BaseVar, ComputedVar, Var, computed_var
|
||||
from reflex.vars import (
|
||||
ComputedVar,
|
||||
ImmutableVarData,
|
||||
Var,
|
||||
computed_var,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from reflex.components.component import Component
|
||||
@ -298,7 +304,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
||||
vars: ClassVar[Dict[str, Var]] = {}
|
||||
|
||||
# The base vars of the class.
|
||||
base_vars: ClassVar[Dict[str, BaseVar]] = {}
|
||||
base_vars: ClassVar[Dict[str, ImmutableVar]] = {}
|
||||
|
||||
# The computed vars of the class.
|
||||
computed_vars: ClassVar[Dict[str, ComputedVar]] = {}
|
||||
@ -520,9 +526,11 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
||||
|
||||
# Set the base and computed vars.
|
||||
cls.base_vars = {
|
||||
f.name: BaseVar(_var_name=f.name, _var_type=f.outer_type_)._var_set_state(
|
||||
cls
|
||||
)
|
||||
f.name: ImmutableVar(
|
||||
_var_name=format.format_state_name(cls.get_full_name()) + "." + f.name,
|
||||
_var_type=f.outer_type_,
|
||||
_var_data=ImmutableVarData.from_state(cls),
|
||||
).guess_type()
|
||||
for f in cls.get_fields().values()
|
||||
if f.name not in cls.get_skip_vars()
|
||||
}
|
||||
@ -846,7 +854,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
||||
return getattr(substate, name)
|
||||
|
||||
@classmethod
|
||||
def _init_var(cls, prop: BaseVar):
|
||||
def _init_var(cls, prop: ImmutableVar):
|
||||
"""Initialize a variable.
|
||||
|
||||
Args:
|
||||
@ -889,7 +897,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
||||
)
|
||||
|
||||
# create the variable based on name and type
|
||||
var = BaseVar(_var_name=name, _var_type=type_)
|
||||
var = ImmutableVar(_var_name=name, _var_type=type_).guess_type()
|
||||
var._var_set_state(cls)
|
||||
|
||||
# add the pydantic field dynamically (must be done before _init_var)
|
||||
@ -909,13 +917,18 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
||||
cls._init_var_dependency_dicts()
|
||||
|
||||
@classmethod
|
||||
def _set_var(cls, prop: BaseVar):
|
||||
def _set_var(cls, prop: ImmutableVar):
|
||||
"""Set the var as a class member.
|
||||
|
||||
Args:
|
||||
prop: The var instance to set.
|
||||
"""
|
||||
setattr(cls, prop._var_name, prop)
|
||||
acutal_var_name = (
|
||||
prop._var_name
|
||||
if "." not in prop._var_name
|
||||
else prop._var_name.split(".")[-1]
|
||||
)
|
||||
setattr(cls, acutal_var_name, prop)
|
||||
|
||||
@classmethod
|
||||
def _create_event_handler(cls, fn):
|
||||
@ -935,7 +948,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
||||
cls.setvar = cls.event_handlers["setvar"] = EventHandlerSetVar(state_cls=cls)
|
||||
|
||||
@classmethod
|
||||
def _create_setter(cls, prop: BaseVar):
|
||||
def _create_setter(cls, prop: ImmutableVar):
|
||||
"""Create a setter for the var.
|
||||
|
||||
Args:
|
||||
@ -948,14 +961,17 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
||||
setattr(cls, setter_name, event_handler)
|
||||
|
||||
@classmethod
|
||||
def _set_default_value(cls, prop: BaseVar):
|
||||
def _set_default_value(cls, prop: ImmutableVar):
|
||||
"""Set the default value for the var.
|
||||
|
||||
Args:
|
||||
prop: The var to set the default value for.
|
||||
"""
|
||||
# Get the pydantic field for the var.
|
||||
field = cls.get_fields()[prop._var_name]
|
||||
if "." in prop._var_name:
|
||||
field = cls.get_fields()[prop._var_name.split(".")[-1]]
|
||||
else:
|
||||
field = cls.get_fields()[prop._var_name]
|
||||
if field.required:
|
||||
default_value = prop.get_default_value()
|
||||
if default_value is not None:
|
||||
|
@ -7,9 +7,11 @@ from typing import Any, Literal, Tuple, Type
|
||||
from reflex import constants
|
||||
from reflex.components.core.breakpoints import Breakpoints, breakpoints_values
|
||||
from reflex.event import EventChain
|
||||
from reflex.ivars.base import ImmutableVar, LiteralVar
|
||||
from reflex.ivars.function import FunctionVar
|
||||
from reflex.utils import format
|
||||
from reflex.utils.imports import ImportVar
|
||||
from reflex.vars import BaseVar, CallableVar, Var, VarData
|
||||
from reflex.vars import ImmutableVarData, Var, VarData
|
||||
|
||||
VarData.update_forward_refs() # Ensure all type definitions are resolved
|
||||
|
||||
@ -25,7 +27,7 @@ color_mode_imports = {
|
||||
}
|
||||
|
||||
|
||||
def _color_mode_var(_var_name: str, _var_type: Type = str) -> BaseVar:
|
||||
def _color_mode_var(_var_name: str, _var_type: Type = str) -> ImmutableVar:
|
||||
"""Create a Var that destructs the _var_name from ColorModeContext.
|
||||
|
||||
Args:
|
||||
@ -33,24 +35,22 @@ def _color_mode_var(_var_name: str, _var_type: Type = str) -> BaseVar:
|
||||
_var_type: The type of the Var.
|
||||
|
||||
Returns:
|
||||
The BaseVar for accessing _var_name from ColorModeContext.
|
||||
The Var that resolves to the color mode.
|
||||
"""
|
||||
return BaseVar(
|
||||
return ImmutableVar(
|
||||
_var_name=_var_name,
|
||||
_var_type=_var_type,
|
||||
_var_is_local=False,
|
||||
_var_is_string=False,
|
||||
_var_data=VarData(
|
||||
_var_data=ImmutableVarData(
|
||||
imports=color_mode_imports,
|
||||
hooks={f"const {{ {_var_name} }} = useContext(ColorModeContext)": None},
|
||||
),
|
||||
)
|
||||
).guess_type()
|
||||
|
||||
|
||||
@CallableVar
|
||||
# @CallableVar
|
||||
def set_color_mode(
|
||||
new_color_mode: LiteralColorMode | Var[LiteralColorMode] | None = None,
|
||||
) -> BaseVar[EventChain]:
|
||||
) -> Var[EventChain]:
|
||||
"""Create an EventChain Var that sets the color mode to a specific value.
|
||||
|
||||
Note: `set_color_mode` is not a real event and cannot be triggered from a
|
||||
@ -70,11 +70,14 @@ def set_color_mode(
|
||||
return base_setter
|
||||
|
||||
if not isinstance(new_color_mode, Var):
|
||||
new_color_mode = Var.create_safe(new_color_mode, _var_is_string=True)
|
||||
return base_setter._replace(
|
||||
_var_name=f"() => {base_setter._var_name}({new_color_mode._var_name_unwrapped})",
|
||||
merge_var_data=new_color_mode._var_data,
|
||||
)
|
||||
new_color_mode = LiteralVar.create(new_color_mode)
|
||||
|
||||
return ImmutableVar(
|
||||
f"() => {str(base_setter)}({str(new_color_mode)})",
|
||||
_var_data=ImmutableVarData.merge(
|
||||
base_setter._get_all_var_data(), new_color_mode._get_all_var_data()
|
||||
),
|
||||
).to(FunctionVar, EventChain)
|
||||
|
||||
|
||||
# Var resolves to the current color mode for the app ("light", "dark" or "system")
|
||||
@ -111,7 +114,9 @@ def media_query(breakpoint_expr: str):
|
||||
return f"@media screen and (min-width: {breakpoint_expr})"
|
||||
|
||||
|
||||
def convert_item(style_item: str | Var) -> tuple[str, VarData | None]:
|
||||
def convert_item(
|
||||
style_item: str | Var,
|
||||
) -> tuple[str, VarData | ImmutableVarData | None]:
|
||||
"""Format a single value in a style dictionary.
|
||||
|
||||
Args:
|
||||
@ -122,13 +127,13 @@ def convert_item(style_item: str | Var) -> tuple[str, VarData | None]:
|
||||
"""
|
||||
if isinstance(style_item, Var):
|
||||
# If the value is a Var, extract the var_data and cast as str.
|
||||
return str(style_item), style_item._var_data
|
||||
return str(style_item), style_item._get_all_var_data()
|
||||
|
||||
# Otherwise, convert to Var to collapse VarData encoded in f-string.
|
||||
new_var = Var.create(style_item, _var_is_string=False)
|
||||
new_var = ImmutableVar.create(style_item)
|
||||
if new_var is not None and new_var._var_data:
|
||||
# The wrapped backtick is used to identify the Var for interpolation.
|
||||
return f"`{str(new_var)}`", new_var._var_data
|
||||
return f"`{str(new_var)}`", new_var._get_all_var_data()
|
||||
|
||||
return style_item, None
|
||||
|
||||
@ -175,7 +180,11 @@ def convert(style_dict):
|
||||
|
||||
for key, value in style_dict.items():
|
||||
keys = format_style_key(key)
|
||||
if isinstance(value, dict):
|
||||
if isinstance(value, Var):
|
||||
return_val = value
|
||||
new_var_data = value._get_all_var_data()
|
||||
update_out_dict(return_val, keys)
|
||||
elif isinstance(value, dict):
|
||||
# Recursively format nested style dictionaries.
|
||||
return_val, new_var_data = convert(value)
|
||||
update_out_dict(return_val, keys)
|
||||
@ -254,7 +263,7 @@ class Style(dict):
|
||||
value: The value to set.
|
||||
"""
|
||||
# Create a Var to collapse VarData encoded in f-string.
|
||||
_var = Var.create(value, _var_is_string=False)
|
||||
_var = ImmutableVar.create(value)
|
||||
if _var is not None:
|
||||
# Carry the imports/hooks when setting a Var as a value.
|
||||
self._var_data = VarData.merge(self._var_data, _var._var_data)
|
||||
|
@ -9,6 +9,8 @@ import re
|
||||
from typing import TYPE_CHECKING, Any, Callable, List, Optional, Union
|
||||
|
||||
from reflex import constants
|
||||
from reflex.ivars.base import ImmutableVar
|
||||
from reflex.ivars.function import FunctionVar
|
||||
from reflex.utils import exceptions, types
|
||||
from reflex.vars import BaseVar, Var
|
||||
|
||||
@ -483,8 +485,14 @@ def format_props(*single_props, **key_value_props) -> list[str]:
|
||||
The formatted props list.
|
||||
"""
|
||||
# Format all the props.
|
||||
from reflex.ivars.base import ImmutableVar
|
||||
|
||||
return [
|
||||
f"{name}={format_prop(prop)}"
|
||||
(
|
||||
f"{name}={{{format_prop(prop)}}}"
|
||||
if isinstance(prop, ImmutableVar)
|
||||
else f"{name}={format_prop(prop)}"
|
||||
)
|
||||
for name, prop in sorted(key_value_props.items())
|
||||
if prop is not None
|
||||
] + [str(prop) for prop in single_props]
|
||||
@ -613,11 +621,13 @@ def format_event_chain(
|
||||
|
||||
|
||||
def format_queue_events(
|
||||
events: EventSpec
|
||||
| EventHandler
|
||||
| Callable
|
||||
| List[EventSpec | EventHandler | Callable]
|
||||
| None = None,
|
||||
events: (
|
||||
EventSpec
|
||||
| EventHandler
|
||||
| Callable
|
||||
| List[EventSpec | EventHandler | Callable]
|
||||
| None
|
||||
) = None,
|
||||
args_spec: Optional[ArgsSpec] = None,
|
||||
) -> Var[EventChain]:
|
||||
"""Format a list of event handler / event spec as a javascript callback.
|
||||
@ -647,9 +657,7 @@ def format_queue_events(
|
||||
)
|
||||
|
||||
if not events:
|
||||
return Var.create_safe(
|
||||
"() => null", _var_is_string=False, _var_is_local=False
|
||||
).to(EventChain)
|
||||
return ImmutableVar("(() => null)").to(FunctionVar, EventChain)
|
||||
|
||||
# If no spec is provided, the function will take no arguments.
|
||||
def _default_args_spec():
|
||||
@ -682,12 +690,10 @@ def format_queue_events(
|
||||
|
||||
# Return the final code snippet, expecting queueEvents, processEvent, and socket to be in scope.
|
||||
# Typically this snippet will _only_ run from within an rx.call_script eval context.
|
||||
return Var.create_safe(
|
||||
return ImmutableVar(
|
||||
f"{arg_def} => {{queueEvents([{','.join(payloads)}], {constants.CompileVars.SOCKET}); "
|
||||
f"processEvent({constants.CompileVars.SOCKET})}}",
|
||||
_var_is_string=False,
|
||||
_var_is_local=False,
|
||||
).to(EventChain)
|
||||
).to(FunctionVar, EventChain)
|
||||
|
||||
|
||||
def format_query_params(router_data: dict[str, Any]) -> dict[str, str]:
|
||||
@ -939,6 +945,6 @@ def format_data_editor_cell(cell: Any):
|
||||
The formatted cell.
|
||||
"""
|
||||
return {
|
||||
"kind": Var.create(value="GridCellKind.Text", _var_is_string=False),
|
||||
"kind": ImmutableVar.create("GridCellKind.Text"),
|
||||
"data": cell,
|
||||
}
|
||||
|
@ -345,6 +345,33 @@ class ImmutableVarData:
|
||||
== imports.collapse_imports(other.imports)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_state(cls, state: Type[BaseState] | str) -> ImmutableVarData:
|
||||
"""Set the state of the var.
|
||||
|
||||
Args:
|
||||
state: The state to set or the full name of the state.
|
||||
|
||||
Returns:
|
||||
The var with the set state.
|
||||
"""
|
||||
from reflex.utils import format
|
||||
|
||||
state_name = state if isinstance(state, str) else state.get_full_name()
|
||||
new_var_data = ImmutableVarData(
|
||||
state=state_name,
|
||||
hooks={
|
||||
"const {0} = useContext(StateContexts.{0})".format(
|
||||
format.format_state_name(state_name)
|
||||
): None
|
||||
},
|
||||
imports={
|
||||
f"/{constants.Dirs.CONTEXTS_PATH}": [ImportVar(tag="StateContexts")],
|
||||
"react": [ImportVar(tag="useContext")],
|
||||
},
|
||||
)
|
||||
return new_var_data
|
||||
|
||||
|
||||
def _decode_var_immutable(value: str) -> tuple[ImmutableVarData | None, str]:
|
||||
"""Decode the state name from a formatted var.
|
||||
@ -800,11 +827,19 @@ class Var:
|
||||
"""
|
||||
from reflex.utils import format
|
||||
|
||||
out = (
|
||||
self._var_full_name
|
||||
if self._var_is_local
|
||||
else format.wrap(self._var_full_name, "{")
|
||||
)
|
||||
if self._var_is_local:
|
||||
console.deprecate(
|
||||
feature_name="Local Vars",
|
||||
reason=(
|
||||
"Setting _var_is_local to True does not have any effect anymore. "
|
||||
"Use the new ImmutableVar instead."
|
||||
),
|
||||
deprecation_version="0.5.9",
|
||||
removal_version="0.6.0",
|
||||
)
|
||||
out = self._var_full_name
|
||||
else:
|
||||
out = format.wrap(self._var_full_name, "{")
|
||||
if self._var_is_string:
|
||||
out = format.format_string(out)
|
||||
return out
|
||||
|
@ -63,6 +63,8 @@ class ImmutableVarData:
|
||||
def merge(
|
||||
cls, *others: ImmutableVarData | VarData | None
|
||||
) -> ImmutableVarData | None: ...
|
||||
@classmethod
|
||||
def from_state(cls, state: Type[BaseState] | str) -> ImmutableVarData: ...
|
||||
|
||||
def _decode_var_immutable(value: str) -> tuple[ImmutableVarData, str]: ...
|
||||
|
||||
@ -150,7 +152,7 @@ class Var:
|
||||
@property
|
||||
def _var_full_name(self) -> str: ...
|
||||
def _var_set_state(self, state: Type[BaseState] | str) -> Any: ...
|
||||
def _get_all_var_data(self) -> VarData: ...
|
||||
def _get_all_var_data(self) -> VarData | ImmutableVarData: ...
|
||||
def json(self) -> str: ...
|
||||
|
||||
@dataclass(eq=False)
|
||||
|
@ -8,19 +8,19 @@ from pandas import DataFrame
|
||||
|
||||
from reflex.base import Base
|
||||
from reflex.constants.base import REFLEX_VAR_CLOSING_TAG, REFLEX_VAR_OPENING_TAG
|
||||
from reflex.experimental.vars.base import (
|
||||
from reflex.ivars.base import (
|
||||
ImmutableVar,
|
||||
LiteralVar,
|
||||
var_operation,
|
||||
)
|
||||
from reflex.experimental.vars.function import ArgsFunctionOperation, FunctionStringVar
|
||||
from reflex.experimental.vars.number import (
|
||||
from reflex.ivars.function import ArgsFunctionOperation, FunctionStringVar
|
||||
from reflex.ivars.number import (
|
||||
LiteralBooleanVar,
|
||||
LiteralNumberVar,
|
||||
NumberVar,
|
||||
)
|
||||
from reflex.experimental.vars.object import LiteralObjectVar
|
||||
from reflex.experimental.vars.sequence import (
|
||||
from reflex.ivars.object import LiteralObjectVar
|
||||
from reflex.ivars.sequence import (
|
||||
ArrayVar,
|
||||
ConcatVarOperation,
|
||||
LiteralArrayVar,
|
||||
|
Loading…
Reference in New Issue
Block a user