Var field cleanup (#1943)
This commit is contained in:
parent
684912e33b
commit
024cb5fa9b
@ -131,8 +131,8 @@ def EventChain():
|
|||||||
@app.add_page
|
@app.add_page
|
||||||
def index():
|
def index():
|
||||||
return rx.fragment(
|
return rx.fragment(
|
||||||
rx.input(value=State.token, readonly=True, id="token"),
|
rx.input(value=State.token, is_read_only=True, id="token"),
|
||||||
rx.input(value=State.interim_value, readonly=True, id="interim_value"),
|
rx.input(value=State.interim_value, is_read_only=True, id="interim_value"),
|
||||||
rx.button(
|
rx.button(
|
||||||
"Return Event",
|
"Return Event",
|
||||||
id="return_event",
|
id="return_event",
|
||||||
|
@ -65,13 +65,13 @@ class Base(pydantic.BaseModel):
|
|||||||
default_value: The default value of the field
|
default_value: The default value of the field
|
||||||
"""
|
"""
|
||||||
new_field = ModelField.infer(
|
new_field = ModelField.infer(
|
||||||
name=var.name,
|
name=var._var_name,
|
||||||
value=default_value,
|
value=default_value,
|
||||||
annotation=var.type_,
|
annotation=var._var_type,
|
||||||
class_validators=None,
|
class_validators=None,
|
||||||
config=cls.__config__,
|
config=cls.__config__,
|
||||||
)
|
)
|
||||||
cls.__fields__.update({var.name: new_field})
|
cls.__fields__.update({var._var_name: new_field})
|
||||||
|
|
||||||
def get_value(self, key: str) -> Any:
|
def get_value(self, key: str) -> Any:
|
||||||
"""Get the value of a field.
|
"""Get the value of a field.
|
||||||
|
@ -243,7 +243,7 @@ def compile_custom_component(
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Concatenate the props.
|
# Concatenate the props.
|
||||||
props = [prop.name for prop in component.get_prop_vars()]
|
props = [prop._var_name for prop in component.get_prop_vars()]
|
||||||
|
|
||||||
# Compile the component.
|
# Compile the component.
|
||||||
return (
|
return (
|
||||||
|
@ -94,4 +94,4 @@ def client_side(javascript_code) -> Var[EventChain]:
|
|||||||
deprecation_version="0.2.9",
|
deprecation_version="0.2.9",
|
||||||
removal_version="0.3.0",
|
removal_version="0.3.0",
|
||||||
)
|
)
|
||||||
return BaseVar(name=f"...args => {{{javascript_code}}}", type_=EventChain)
|
return BaseVar(_var_name=f"...args => {{{javascript_code}}}", _var_type=EventChain)
|
||||||
|
@ -155,7 +155,7 @@ class Component(Base, ABC):
|
|||||||
raise TypeError
|
raise TypeError
|
||||||
|
|
||||||
# Get the passed type and the var type.
|
# Get the passed type and the var type.
|
||||||
passed_type = kwargs[key].type_
|
passed_type = kwargs[key]._var_type
|
||||||
expected_type = fields[key].outer_type_.__args__[0]
|
expected_type = fields[key].outer_type_.__args__[0]
|
||||||
except TypeError:
|
except TypeError:
|
||||||
# If it is not a valid var, check the base types.
|
# If it is not a valid var, check the base types.
|
||||||
@ -222,7 +222,7 @@ class Component(Base, ABC):
|
|||||||
|
|
||||||
# If it's an event chain var, return it.
|
# If it's an event chain var, return it.
|
||||||
if isinstance(value, Var):
|
if isinstance(value, Var):
|
||||||
if value.type_ is not EventChain:
|
if value._var_type is not EventChain:
|
||||||
raise ValueError(f"Invalid event chain: {value}")
|
raise ValueError(f"Invalid event chain: {value}")
|
||||||
return value
|
return value
|
||||||
|
|
||||||
@ -388,7 +388,7 @@ class Component(Base, ABC):
|
|||||||
# Add ref to element if `id` is not None.
|
# Add ref to element if `id` is not None.
|
||||||
ref = self.get_ref()
|
ref = self.get_ref()
|
||||||
if ref is not None:
|
if ref is not None:
|
||||||
props["ref"] = Var.create(ref, is_local=False)
|
props["ref"] = Var.create(ref, _var_is_local=False)
|
||||||
|
|
||||||
return tag.add_props(**props)
|
return tag.add_props(**props)
|
||||||
|
|
||||||
@ -440,7 +440,7 @@ class Component(Base, ABC):
|
|||||||
children = [
|
children = [
|
||||||
child
|
child
|
||||||
if isinstance(child, Component)
|
if isinstance(child, Component)
|
||||||
else Bare.create(contents=Var.create(child, is_string=True))
|
else Bare.create(contents=Var.create(child, _var_is_string=True))
|
||||||
for child in children
|
for child in children
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -808,11 +808,13 @@ class CustomComponent(Component):
|
|||||||
# Handle subclasses of Base.
|
# Handle subclasses of Base.
|
||||||
if types._issubclass(type_, Base):
|
if types._issubclass(type_, Base):
|
||||||
try:
|
try:
|
||||||
value = BaseVar(name=value.json(), type_=type_, is_local=True)
|
value = BaseVar(
|
||||||
|
_var_name=value.json(), _var_type=type_, _var_is_local=True
|
||||||
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
value = Var.create(value)
|
value = Var.create(value)
|
||||||
else:
|
else:
|
||||||
value = Var.create(value, is_string=type(value) is str)
|
value = Var.create(value, _var_is_string=type(value) is str)
|
||||||
|
|
||||||
# Set the prop.
|
# Set the prop.
|
||||||
self.props[format.to_camel_case(key)] = value
|
self.props[format.to_camel_case(key)] = value
|
||||||
@ -888,8 +890,10 @@ class CustomComponent(Component):
|
|||||||
"""
|
"""
|
||||||
return [
|
return [
|
||||||
BaseVar(
|
BaseVar(
|
||||||
name=name,
|
_var_name=name,
|
||||||
type_=prop.type_ if types._isinstance(prop, Var) else type(prop),
|
_var_type=prop._var_type
|
||||||
|
if types._isinstance(prop, Var)
|
||||||
|
else type(prop),
|
||||||
)
|
)
|
||||||
for name, prop in self.props.items()
|
for name, prop in self.props.items()
|
||||||
]
|
]
|
||||||
|
@ -48,7 +48,8 @@ class CodeBlock(Component):
|
|||||||
merged_imports = super()._get_imports()
|
merged_imports = super()._get_imports()
|
||||||
if self.theme is not None:
|
if self.theme is not None:
|
||||||
merged_imports = imports.merge_imports(
|
merged_imports = imports.merge_imports(
|
||||||
merged_imports, {PRISM_STYLES_PATH: {ImportVar(tag=self.theme.name)}}
|
merged_imports,
|
||||||
|
{PRISM_STYLES_PATH: {ImportVar(tag=self.theme._var_name)}},
|
||||||
)
|
)
|
||||||
return merged_imports
|
return merged_imports
|
||||||
|
|
||||||
@ -113,7 +114,7 @@ class CodeBlock(Component):
|
|||||||
out = super()._render()
|
out = super()._render()
|
||||||
if self.theme is not None:
|
if self.theme is not None:
|
||||||
out.add_props(
|
out.add_props(
|
||||||
style=Var.create(self.theme.name, is_local=False)
|
style=Var.create(self.theme._var_name, _var_is_local=False)
|
||||||
).remove_props("theme")
|
).remove_props("theme")
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ class DataTable(Gridjs):
|
|||||||
|
|
||||||
# The annotation should be provided if data is a computed var. We need this to know how to
|
# The annotation should be provided if data is a computed var. We need this to know how to
|
||||||
# render pandas dataframes.
|
# render pandas dataframes.
|
||||||
if isinstance(data, ComputedVar) and data.type_ == Any:
|
if isinstance(data, ComputedVar) and data._var_type == Any:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"Annotation of the computed var assigned to the data field should be provided."
|
"Annotation of the computed var assigned to the data field should be provided."
|
||||||
)
|
)
|
||||||
@ -72,7 +72,7 @@ class DataTable(Gridjs):
|
|||||||
if (
|
if (
|
||||||
columns is not None
|
columns is not None
|
||||||
and isinstance(columns, ComputedVar)
|
and isinstance(columns, ComputedVar)
|
||||||
and columns.type_ == Any
|
and columns._var_type == Any
|
||||||
):
|
):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"Annotation of the computed var assigned to the column field should be provided."
|
"Annotation of the computed var assigned to the column field should be provided."
|
||||||
@ -81,7 +81,7 @@ class DataTable(Gridjs):
|
|||||||
# If data is a pandas dataframe and columns are provided throw an error.
|
# If data is a pandas dataframe and columns are provided throw an error.
|
||||||
if (
|
if (
|
||||||
types.is_dataframe(type(data))
|
types.is_dataframe(type(data))
|
||||||
or (isinstance(data, Var) and types.is_dataframe(data.type_))
|
or (isinstance(data, Var) and types.is_dataframe(data._var_type))
|
||||||
) and columns is not None:
|
) and columns is not None:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"Cannot pass in both a pandas dataframe and columns to the data_table component."
|
"Cannot pass in both a pandas dataframe and columns to the data_table component."
|
||||||
@ -89,7 +89,7 @@ class DataTable(Gridjs):
|
|||||||
|
|
||||||
# If data is a list and columns are not provided, throw an error
|
# If data is a list and columns are not provided, throw an error
|
||||||
if (
|
if (
|
||||||
(isinstance(data, Var) and types._issubclass(data.type_, List))
|
(isinstance(data, Var) and types._issubclass(data._var_type, List))
|
||||||
or issubclass(type(data), List)
|
or issubclass(type(data), List)
|
||||||
) and columns is None:
|
) and columns is None:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
@ -109,16 +109,16 @@ class DataTable(Gridjs):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def _render(self) -> Tag:
|
def _render(self) -> Tag:
|
||||||
if isinstance(self.data, Var) and types.is_dataframe(self.data.type_):
|
if isinstance(self.data, Var) and types.is_dataframe(self.data._var_type):
|
||||||
self.columns = BaseVar(
|
self.columns = BaseVar(
|
||||||
name=f"{self.data.name}.columns",
|
_var_name=f"{self.data._var_name}.columns",
|
||||||
type_=List[Any],
|
_var_type=List[Any],
|
||||||
state=self.data.state,
|
_var_state=self.data._var_state,
|
||||||
)
|
)
|
||||||
self.data = BaseVar(
|
self.data = BaseVar(
|
||||||
name=f"{self.data.name}.data",
|
_var_name=f"{self.data._var_name}.data",
|
||||||
type_=List[List[Any]],
|
_var_type=List[List[Any]],
|
||||||
state=self.data.state,
|
_var_state=self.data._var_state,
|
||||||
)
|
)
|
||||||
if types.is_dataframe(type(self.data)):
|
if types.is_dataframe(type(self.data)):
|
||||||
# If given a pandas df break up the data and columns
|
# If given a pandas df break up the data and columns
|
||||||
|
@ -101,7 +101,9 @@ class Thead(ChakraComponent):
|
|||||||
if (
|
if (
|
||||||
(
|
(
|
||||||
isinstance(headers, Var)
|
isinstance(headers, Var)
|
||||||
and not types.check_type_in_allowed_types(headers.type_, allowed_types)
|
and not types.check_type_in_allowed_types(
|
||||||
|
headers._var_type, allowed_types
|
||||||
|
)
|
||||||
)
|
)
|
||||||
or not isinstance(headers, Var)
|
or not isinstance(headers, Var)
|
||||||
and not types.check_type_in_allowed_types(type(headers), allowed_types)
|
and not types.check_type_in_allowed_types(type(headers), allowed_types)
|
||||||
@ -156,7 +158,7 @@ class Tbody(ChakraComponent):
|
|||||||
"""
|
"""
|
||||||
allowed_subclasses = (List, Tuple)
|
allowed_subclasses = (List, Tuple)
|
||||||
if isinstance(rows, Var):
|
if isinstance(rows, Var):
|
||||||
outer_type = rows.type_
|
outer_type = rows._var_type
|
||||||
inner_type = (
|
inner_type = (
|
||||||
outer_type.__args__[0] if hasattr(outer_type, "__args__") else None
|
outer_type.__args__[0] if hasattr(outer_type, "__args__") else None
|
||||||
)
|
)
|
||||||
@ -222,7 +224,9 @@ class Tfoot(ChakraComponent):
|
|||||||
if (
|
if (
|
||||||
(
|
(
|
||||||
isinstance(footers, Var)
|
isinstance(footers, Var)
|
||||||
and not types.check_type_in_allowed_types(footers.type_, allowed_types)
|
and not types.check_type_in_allowed_types(
|
||||||
|
footers._var_type, allowed_types
|
||||||
|
)
|
||||||
)
|
)
|
||||||
or not isinstance(footers, Var)
|
or not isinstance(footers, Var)
|
||||||
and not types.check_type_in_allowed_types(type(footers), allowed_types)
|
and not types.check_type_in_allowed_types(type(footers), allowed_types)
|
||||||
|
@ -58,7 +58,7 @@ class DebounceInput(Component):
|
|||||||
raise ValueError("DebounceInput child requires an on_change handler")
|
raise ValueError("DebounceInput child requires an on_change handler")
|
||||||
child_ref = child.get_ref()
|
child_ref = child.get_ref()
|
||||||
if child_ref and not props.get("ref"):
|
if child_ref and not props.get("ref"):
|
||||||
props["input_ref"] = Var.create(child_ref, is_local=False)
|
props["input_ref"] = Var.create(child_ref, _var_is_local=False)
|
||||||
self.children = []
|
self.children = []
|
||||||
tag = super()._render()
|
tag = super()._render()
|
||||||
tag.add_props(
|
tag.add_props(
|
||||||
@ -67,7 +67,9 @@ class DebounceInput(Component):
|
|||||||
sx=child.style,
|
sx=child.style,
|
||||||
id=child.id,
|
id=child.id,
|
||||||
class_name=child.class_name,
|
class_name=child.class_name,
|
||||||
element=Var.create("{%s}" % child.tag, is_local=False, is_string=False),
|
element=Var.create(
|
||||||
|
"{%s}" % child.tag, _var_is_local=False, _var_is_string=False
|
||||||
|
),
|
||||||
)
|
)
|
||||||
# do NOT render the child, DebounceInput will create it
|
# do NOT render the child, DebounceInput will create it
|
||||||
object.__setattr__(child, "render", lambda: "")
|
object.__setattr__(child, "render", lambda: "")
|
||||||
|
@ -29,10 +29,12 @@ class Form(ChakraComponent):
|
|||||||
# to collect data
|
# to collect data
|
||||||
if ref.startswith("refs_"):
|
if ref.startswith("refs_"):
|
||||||
form_refs[ref[5:-3]] = Var.create(
|
form_refs[ref[5:-3]] = Var.create(
|
||||||
f"getRefValues({ref[:-3]})", is_local=False
|
f"getRefValues({ref[:-3]})", _var_is_local=False
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
form_refs[ref[4:]] = Var.create(f"getRefValue({ref})", is_local=False)
|
form_refs[ref[4:]] = Var.create(
|
||||||
|
f"getRefValue({ref})", _var_is_local=False
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
**super().get_event_triggers(),
|
**super().get_event_triggers(),
|
||||||
|
@ -308,7 +308,9 @@ class Select(Component):
|
|||||||
return {
|
return {
|
||||||
**super().get_event_triggers(),
|
**super().get_event_triggers(),
|
||||||
EventTriggers.ON_CHANGE: (
|
EventTriggers.ON_CHANGE: (
|
||||||
lambda e0: [Var.create_safe(f"{e0}.map(e => e.value)", is_local=True)]
|
lambda e0: [
|
||||||
|
Var.create_safe(f"{e0}.map(e => e.value)", _var_is_local=True)
|
||||||
|
]
|
||||||
if self.is_multi
|
if self.is_multi
|
||||||
else lambda e0: [e0]
|
else lambda e0: [e0]
|
||||||
),
|
),
|
||||||
|
@ -11,13 +11,17 @@ from reflex.event import EventChain
|
|||||||
from reflex.vars import BaseVar, Var
|
from reflex.vars import BaseVar, Var
|
||||||
|
|
||||||
files_state: str = "const [files, setFiles] = useState([]);"
|
files_state: str = "const [files, setFiles] = useState([]);"
|
||||||
upload_file: BaseVar = BaseVar(name="e => setFiles((files) => e)", type_=EventChain)
|
upload_file: BaseVar = BaseVar(
|
||||||
|
_var_name="e => setFiles((files) => e)", _var_type=EventChain
|
||||||
|
)
|
||||||
|
|
||||||
# Use this var along with the Upload component to render the list of selected files.
|
# Use this var along with the Upload component to render the list of selected files.
|
||||||
selected_files: BaseVar = BaseVar(name="files.map((f) => f.name)", type_=List[str])
|
selected_files: BaseVar = BaseVar(
|
||||||
|
_var_name="files.map((f) => f.name)", _var_type=List[str]
|
||||||
|
)
|
||||||
|
|
||||||
clear_selected_files: BaseVar = BaseVar(
|
clear_selected_files: BaseVar = BaseVar(
|
||||||
name="_e => setFiles((files) => [])", type_=EventChain
|
_var_name="_e => setFiles((files) => [])", _var_type=EventChain
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -77,7 +81,9 @@ class Upload(Component):
|
|||||||
}
|
}
|
||||||
# The file input to use.
|
# The file input to use.
|
||||||
upload = Input.create(type_="file")
|
upload = Input.create(type_="file")
|
||||||
upload.special_props = {BaseVar(name="{...getInputProps()}", type_=None)}
|
upload.special_props = {
|
||||||
|
BaseVar(_var_name="{...getInputProps()}", _var_type=None)
|
||||||
|
}
|
||||||
|
|
||||||
# The dropzone to use.
|
# The dropzone to use.
|
||||||
zone = Box.create(
|
zone = Box.create(
|
||||||
@ -85,7 +91,7 @@ class Upload(Component):
|
|||||||
*children,
|
*children,
|
||||||
**{k: v for k, v in props.items() if k not in supported_props},
|
**{k: v for k, v in props.items() if k not in supported_props},
|
||||||
)
|
)
|
||||||
zone.special_props = {BaseVar(name="{...getRootProps()}", type_=None)}
|
zone.special_props = {BaseVar(_var_name="{...getRootProps()}", _var_type=None)}
|
||||||
|
|
||||||
# Create the component.
|
# Create the component.
|
||||||
return super().create(zone, on_drop=upload_file, **upload_props)
|
return super().create(zone, on_drop=upload_file, **upload_props)
|
||||||
|
@ -77,7 +77,7 @@ class Cond(Component):
|
|||||||
).set(
|
).set(
|
||||||
props=tag.format_props(),
|
props=tag.format_props(),
|
||||||
),
|
),
|
||||||
cond_state=f"isTrue({self.cond.full_name})",
|
cond_state=f"isTrue({self.cond._var_full_name})",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -118,11 +118,11 @@ def cond(condition: Any, c1: Any, c2: Any = None):
|
|||||||
|
|
||||||
# Create the conditional var.
|
# Create the conditional var.
|
||||||
return BaseVar(
|
return BaseVar(
|
||||||
name=format.format_cond(
|
_var_name=format.format_cond(
|
||||||
cond=cond_var.full_name,
|
cond=cond_var._var_full_name,
|
||||||
true_value=c1,
|
true_value=c1,
|
||||||
false_value=c2,
|
false_value=c2,
|
||||||
is_prop=True,
|
is_prop=True,
|
||||||
),
|
),
|
||||||
type_=c1.type_ if isinstance(c1, BaseVar) else type(c1),
|
_var_type=c1._var_type if isinstance(c1, BaseVar) else type(c1),
|
||||||
)
|
)
|
||||||
|
@ -35,18 +35,18 @@ class Foreach(Component):
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
type_ = (
|
type_ = (
|
||||||
iterable.type_
|
iterable._var_type
|
||||||
if iterable.type_.mro()[0] == dict
|
if iterable._var_type.mro()[0] == dict
|
||||||
else iterable.type_.__args__[0]
|
else iterable._var_type.__args__[0]
|
||||||
)
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
type_ = Any
|
type_ = Any
|
||||||
iterable = Var.create(iterable) # type: ignore
|
iterable = Var.create(iterable) # type: ignore
|
||||||
if iterable.type_ == Any:
|
if iterable._var_type == Any:
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
f"Could not foreach over var of type Any. (If you are trying to foreach over a state var, add a type annotation to the var.)"
|
f"Could not foreach over var of type Any. (If you are trying to foreach over a state var, add a type annotation to the var.)"
|
||||||
)
|
)
|
||||||
arg = BaseVar(name="_", type_=type_, is_local=True)
|
arg = BaseVar(_var_name="_", _var_type=type_, _var_is_local=True)
|
||||||
return cls(
|
return cls(
|
||||||
iterable=iterable,
|
iterable=iterable,
|
||||||
render_fn=render_fn,
|
render_fn=render_fn,
|
||||||
@ -66,15 +66,15 @@ class Foreach(Component):
|
|||||||
tag = self._render()
|
tag = self._render()
|
||||||
try:
|
try:
|
||||||
type_ = (
|
type_ = (
|
||||||
self.iterable.type_
|
self.iterable._var_type
|
||||||
if self.iterable.type_.mro()[0] == dict
|
if self.iterable._var_type.mro()[0] == dict
|
||||||
else self.iterable.type_.__args__[0]
|
else self.iterable._var_type.__args__[0]
|
||||||
)
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
type_ = Any
|
type_ = Any
|
||||||
arg = BaseVar(
|
arg = BaseVar(
|
||||||
name=get_unique_variable_name(),
|
_var_name=get_unique_variable_name(),
|
||||||
type_=type_,
|
_var_type=type_,
|
||||||
)
|
)
|
||||||
index_arg = tag.get_index_var_arg()
|
index_arg = tag.get_index_var_arg()
|
||||||
component = tag.render_component(self.render_fn, arg)
|
component = tag.render_component(self.render_fn, arg)
|
||||||
@ -89,8 +89,8 @@ class Foreach(Component):
|
|||||||
children=[component.render()],
|
children=[component.render()],
|
||||||
props=tag.format_props(),
|
props=tag.format_props(),
|
||||||
),
|
),
|
||||||
iterable_state=tag.iterable.full_name,
|
iterable_state=tag.iterable._var_full_name,
|
||||||
arg_name=arg.name,
|
arg_name=arg._var_name,
|
||||||
arg_index=index_arg,
|
arg_index=index_arg,
|
||||||
iterable_type=tag.iterable.type_.mro()[0].__name__,
|
iterable_type=tag.iterable._var_type.mro()[0].__name__,
|
||||||
)
|
)
|
||||||
|
@ -77,7 +77,7 @@ class Image(ChakraComponent):
|
|||||||
"""
|
"""
|
||||||
src = props.get("src", None)
|
src = props.get("src", None)
|
||||||
if src is not None and not isinstance(src, (Var)):
|
if src is not None and not isinstance(src, (Var)):
|
||||||
props["src"] = Var.create(value=src, is_string=True)
|
props["src"] = Var.create(value=src, _var_is_string=True)
|
||||||
return super().create(*children, **props)
|
return super().create(*children, **props)
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ class Link(ChakraComponent):
|
|||||||
text: Var[str]
|
text: Var[str]
|
||||||
|
|
||||||
# What the link renders to.
|
# What the link renders to.
|
||||||
as_: Var[str] = BaseVar.create("{NextLink}", is_local=False) # type: ignore
|
as_: Var[str] = BaseVar.create(value="{NextLink}", _var_is_local=False) # type: ignore
|
||||||
|
|
||||||
# If true, the link will open in new tab.
|
# If true, the link will open in new tab.
|
||||||
is_external: Var[bool]
|
is_external: Var[bool]
|
||||||
|
@ -13,14 +13,14 @@ from reflex.vars import ImportVar, Var
|
|||||||
|
|
||||||
connection_error: Var = Var.create_safe(
|
connection_error: Var = Var.create_safe(
|
||||||
value="(connectError !== null) ? connectError.message : ''",
|
value="(connectError !== null) ? connectError.message : ''",
|
||||||
is_local=False,
|
_var_is_local=False,
|
||||||
is_string=False,
|
_var_is_string=False,
|
||||||
)
|
)
|
||||||
has_connection_error: Var = Var.create_safe(
|
has_connection_error: Var = Var.create_safe(
|
||||||
value="connectError !== null",
|
value="connectError !== null",
|
||||||
is_string=False,
|
_var_is_string=False,
|
||||||
)
|
)
|
||||||
has_connection_error.type_ = bool
|
has_connection_error._var_type = bool
|
||||||
|
|
||||||
|
|
||||||
class WebsocketTargetURL(Bare):
|
class WebsocketTargetURL(Bare):
|
||||||
|
@ -31,8 +31,8 @@ class IterTag(Tag):
|
|||||||
The index var.
|
The index var.
|
||||||
"""
|
"""
|
||||||
return BaseVar(
|
return BaseVar(
|
||||||
name=INDEX_VAR,
|
_var_name=INDEX_VAR,
|
||||||
type_=int,
|
_var_type=int,
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -43,9 +43,9 @@ class IterTag(Tag):
|
|||||||
The index var.
|
The index var.
|
||||||
"""
|
"""
|
||||||
return BaseVar(
|
return BaseVar(
|
||||||
name=INDEX_VAR,
|
_var_name=INDEX_VAR,
|
||||||
type_=int,
|
_var_type=int,
|
||||||
is_local=True,
|
_var_is_local=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -17,18 +17,18 @@ from reflex.utils import console, imports, types
|
|||||||
from reflex.vars import ImportVar, Var
|
from reflex.vars import ImportVar, Var
|
||||||
|
|
||||||
# Special vars used in the component map.
|
# Special vars used in the component map.
|
||||||
_CHILDREN = Var.create_safe("children", is_local=False)
|
_CHILDREN = Var.create_safe("children", _var_is_local=False)
|
||||||
_PROPS = Var.create_safe("...props", is_local=False)
|
_PROPS = Var.create_safe("...props", _var_is_local=False)
|
||||||
_MOCK_ARG = Var.create_safe("")
|
_MOCK_ARG = Var.create_safe("")
|
||||||
|
|
||||||
# Special remark plugins.
|
# Special remark plugins.
|
||||||
_REMARK_MATH = Var.create_safe("remarkMath", is_local=False)
|
_REMARK_MATH = Var.create_safe("remarkMath", _var_is_local=False)
|
||||||
_REMARK_GFM = Var.create_safe("remarkGfm", is_local=False)
|
_REMARK_GFM = Var.create_safe("remarkGfm", _var_is_local=False)
|
||||||
_REMARK_PLUGINS = Var.create_safe([_REMARK_MATH, _REMARK_GFM])
|
_REMARK_PLUGINS = Var.create_safe([_REMARK_MATH, _REMARK_GFM])
|
||||||
|
|
||||||
# Special rehype plugins.
|
# Special rehype plugins.
|
||||||
_REHYPE_KATEX = Var.create_safe("rehypeKatex", is_local=False)
|
_REHYPE_KATEX = Var.create_safe("rehypeKatex", _var_is_local=False)
|
||||||
_REHYPE_RAW = Var.create_safe("rehypeRaw", is_local=False)
|
_REHYPE_RAW = Var.create_safe("rehypeRaw", _var_is_local=False)
|
||||||
_REHYPE_PLUGINS = Var.create_safe([_REHYPE_KATEX, _REHYPE_RAW])
|
_REHYPE_PLUGINS = Var.create_safe([_REHYPE_KATEX, _REHYPE_RAW])
|
||||||
|
|
||||||
# Component Mapping
|
# Component Mapping
|
||||||
@ -153,13 +153,17 @@ class Markdown(Component):
|
|||||||
{
|
{
|
||||||
"": {ImportVar(tag="katex/dist/katex.min.css")},
|
"": {ImportVar(tag="katex/dist/katex.min.css")},
|
||||||
"remark-math@5.1.1": {
|
"remark-math@5.1.1": {
|
||||||
ImportVar(tag=_REMARK_MATH.name, is_default=True)
|
ImportVar(tag=_REMARK_MATH._var_name, is_default=True)
|
||||||
|
},
|
||||||
|
"remark-gfm@3.0.1": {
|
||||||
|
ImportVar(tag=_REMARK_GFM._var_name, is_default=True)
|
||||||
},
|
},
|
||||||
"remark-gfm@3.0.1": {ImportVar(tag=_REMARK_GFM.name, is_default=True)},
|
|
||||||
"rehype-katex@6.0.3": {
|
"rehype-katex@6.0.3": {
|
||||||
ImportVar(tag=_REHYPE_KATEX.name, is_default=True)
|
ImportVar(tag=_REHYPE_KATEX._var_name, is_default=True)
|
||||||
|
},
|
||||||
|
"rehype-raw@6.1.1": {
|
||||||
|
ImportVar(tag=_REHYPE_RAW._var_name, is_default=True)
|
||||||
},
|
},
|
||||||
"rehype-raw@6.1.1": {ImportVar(tag=_REHYPE_RAW.name, is_default=True)},
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -226,20 +230,20 @@ class Markdown(Component):
|
|||||||
The formatted component map.
|
The formatted component map.
|
||||||
"""
|
"""
|
||||||
components = {
|
components = {
|
||||||
tag: f"{{({{{_CHILDREN.name}, {_PROPS.name}}}) => {self.format_component(tag)}}}"
|
tag: f"{{({{{_CHILDREN._var_name}, {_PROPS._var_name}}}) => {self.format_component(tag)}}}"
|
||||||
for tag in self.component_map
|
for tag in self.component_map
|
||||||
}
|
}
|
||||||
|
|
||||||
# Separate out inline code and code blocks.
|
# Separate out inline code and code blocks.
|
||||||
components[
|
components[
|
||||||
"code"
|
"code"
|
||||||
] = f"""{{({{inline, className, {_CHILDREN.name}, {_PROPS.name}}}) => {{
|
] = f"""{{({{inline, className, {_CHILDREN._var_name}, {_PROPS._var_name}}}) => {{
|
||||||
const match = (className || '').match(/language-(?<lang>.*)/);
|
const match = (className || '').match(/language-(?<lang>.*)/);
|
||||||
const language = match ? match[1] : '';
|
const language = match ? match[1] : '';
|
||||||
return inline ? (
|
return inline ? (
|
||||||
{self.format_component("code")}
|
{self.format_component("code")}
|
||||||
) : (
|
) : (
|
||||||
{self.format_component("codeblock", language=Var.create_safe("language", is_local=False), children=Var.create_safe("String(children)", is_local=False))}
|
{self.format_component("codeblock", language=Var.create_safe("language", _var_is_local=False), children=Var.create_safe("String(children)", _var_is_local=False))}
|
||||||
);
|
);
|
||||||
}}}}""".replace(
|
}}}}""".replace(
|
||||||
"\n", " "
|
"\n", " "
|
||||||
|
@ -153,7 +153,7 @@ class EventHandler(Base):
|
|||||||
|
|
||||||
# Otherwise, convert to JSON.
|
# Otherwise, convert to JSON.
|
||||||
try:
|
try:
|
||||||
values.append(Var.create(arg, is_string=type(arg) is str))
|
values.append(Var.create(arg, _var_is_string=type(arg) is str))
|
||||||
except TypeError as e:
|
except TypeError as e:
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
f"Arguments to event handlers must be Vars or JSON-serializable. Got {arg} of type {type(arg)}."
|
f"Arguments to event handlers must be Vars or JSON-serializable. Got {arg} of type {type(arg)}."
|
||||||
@ -211,7 +211,7 @@ class FrontendEvent(Base):
|
|||||||
|
|
||||||
|
|
||||||
# The default event argument.
|
# The default event argument.
|
||||||
EVENT_ARG = BaseVar(name="_e", type_=FrontendEvent, is_local=True)
|
EVENT_ARG = BaseVar(_var_name="_e", _var_type=FrontendEvent, _var_is_local=True)
|
||||||
|
|
||||||
|
|
||||||
class FileUpload(Base):
|
class FileUpload(Base):
|
||||||
@ -241,7 +241,7 @@ def server_side(name: str, sig: inspect.Signature, **kwargs) -> EventSpec:
|
|||||||
return EventSpec(
|
return EventSpec(
|
||||||
handler=EventHandler(fn=fn),
|
handler=EventHandler(fn=fn),
|
||||||
args=tuple(
|
args=tuple(
|
||||||
(Var.create_safe(k), Var.create_safe(v, is_string=type(v) is str))
|
(Var.create_safe(k), Var.create_safe(v, _var_is_string=type(v) is str))
|
||||||
for k, v in kwargs.items()
|
for k, v in kwargs.items()
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -560,9 +560,9 @@ def parse_args_spec(arg_spec: ArgsSpec):
|
|||||||
return arg_spec(
|
return arg_spec(
|
||||||
*[
|
*[
|
||||||
BaseVar(
|
BaseVar(
|
||||||
name=f"_{l_arg}",
|
_var_name=f"_{l_arg}",
|
||||||
type_=spec.annotations.get(l_arg, FrontendEvent),
|
_var_type=spec.annotations.get(l_arg, FrontendEvent),
|
||||||
is_local=True,
|
_var_is_local=True,
|
||||||
)
|
)
|
||||||
for l_arg in spec.args
|
for l_arg in spec.args
|
||||||
]
|
]
|
||||||
@ -675,7 +675,7 @@ def fix_events(
|
|||||||
e = e()
|
e = e()
|
||||||
assert isinstance(e, EventSpec), f"Unexpected event type, {type(e)}."
|
assert isinstance(e, EventSpec), f"Unexpected event type, {type(e)}."
|
||||||
name = format.format_event_handler(e.handler)
|
name = format.format_event_handler(e.handler)
|
||||||
payload = {k.name: v._decode() for k, v in e.args} # type: ignore
|
payload = {k._var_name: v._decode() for k, v in e.args} # type: ignore
|
||||||
|
|
||||||
# Create an event and append it to the list.
|
# Create an event and append it to the list.
|
||||||
out.append(
|
out.append(
|
||||||
|
@ -132,7 +132,7 @@ class State(Base, ABC, extra=pydantic.Extra.allow):
|
|||||||
)
|
)
|
||||||
for cvar_name, cvar in self.computed_vars.items():
|
for cvar_name, cvar in self.computed_vars.items():
|
||||||
# Add the dependencies.
|
# Add the dependencies.
|
||||||
for var in cvar.deps(objclass=type(self)):
|
for var in cvar._deps(objclass=type(self)):
|
||||||
self.computed_var_dependencies[var].add(cvar_name)
|
self.computed_var_dependencies[var].add(cvar_name)
|
||||||
if var in inherited_vars:
|
if var in inherited_vars:
|
||||||
# track that this substate depends on its parent for this var
|
# track that this substate depends on its parent for this var
|
||||||
@ -211,12 +211,14 @@ class State(Base, ABC, extra=pydantic.Extra.allow):
|
|||||||
|
|
||||||
# Set the base and computed vars.
|
# Set the base and computed vars.
|
||||||
cls.base_vars = {
|
cls.base_vars = {
|
||||||
f.name: BaseVar(name=f.name, type_=f.outer_type_).set_state(cls)
|
f.name: BaseVar(_var_name=f.name, _var_type=f.outer_type_)._var_set_state(
|
||||||
|
cls
|
||||||
|
)
|
||||||
for f in cls.get_fields().values()
|
for f in cls.get_fields().values()
|
||||||
if f.name not in cls.get_skip_vars()
|
if f.name not in cls.get_skip_vars()
|
||||||
}
|
}
|
||||||
cls.computed_vars = {
|
cls.computed_vars = {
|
||||||
v.name: v.set_state(cls)
|
v._var_name: v._var_set_state(cls)
|
||||||
for v in cls.__dict__.values()
|
for v in cls.__dict__.values()
|
||||||
if isinstance(v, ComputedVar)
|
if isinstance(v, ComputedVar)
|
||||||
}
|
}
|
||||||
@ -389,12 +391,12 @@ class State(Base, ABC, extra=pydantic.Extra.allow):
|
|||||||
Raises:
|
Raises:
|
||||||
TypeError: if the variable has an incorrect type
|
TypeError: if the variable has an incorrect type
|
||||||
"""
|
"""
|
||||||
if not types.is_valid_var_type(prop.type_):
|
if not types.is_valid_var_type(prop._var_type):
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
"State vars must be primitive Python types, "
|
"State vars must be primitive Python types, "
|
||||||
"Plotly figures, Pandas dataframes, "
|
"Plotly figures, Pandas dataframes, "
|
||||||
"or subclasses of rx.Base. "
|
"or subclasses of rx.Base. "
|
||||||
f'Found var "{prop.name}" with type {prop.type_}.'
|
f'Found var "{prop._var_name}" with type {prop._var_type}.'
|
||||||
)
|
)
|
||||||
cls._set_var(prop)
|
cls._set_var(prop)
|
||||||
cls._create_setter(prop)
|
cls._create_setter(prop)
|
||||||
@ -421,8 +423,8 @@ class State(Base, ABC, extra=pydantic.Extra.allow):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# create the variable based on name and type
|
# create the variable based on name and type
|
||||||
var = BaseVar(name=name, type_=type_)
|
var = BaseVar(_var_name=name, _var_type=type_)
|
||||||
var.set_state(cls)
|
var._var_set_state(cls)
|
||||||
|
|
||||||
# add the pydantic field dynamically (must be done before _init_var)
|
# add the pydantic field dynamically (must be done before _init_var)
|
||||||
cls.add_field(var, default_value)
|
cls.add_field(var, default_value)
|
||||||
@ -444,7 +446,7 @@ class State(Base, ABC, extra=pydantic.Extra.allow):
|
|||||||
Args:
|
Args:
|
||||||
prop: The var instance to set.
|
prop: The var instance to set.
|
||||||
"""
|
"""
|
||||||
setattr(cls, prop.name, prop)
|
setattr(cls, prop._var_name, prop)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _create_setter(cls, prop: BaseVar):
|
def _create_setter(cls, prop: BaseVar):
|
||||||
@ -467,7 +469,7 @@ class State(Base, ABC, extra=pydantic.Extra.allow):
|
|||||||
prop: The var to set the default value for.
|
prop: The var to set the default value for.
|
||||||
"""
|
"""
|
||||||
# Get the pydantic field for the var.
|
# Get the pydantic field for the var.
|
||||||
field = cls.get_fields()[prop.name]
|
field = cls.get_fields()[prop._var_name]
|
||||||
default_value = prop.get_default_value()
|
default_value = prop.get_default_value()
|
||||||
if field.required and default_value is not None:
|
if field.required and default_value is not None:
|
||||||
field.required = False
|
field.required = False
|
||||||
@ -599,8 +601,9 @@ class State(Base, ABC, extra=pydantic.Extra.allow):
|
|||||||
func = arglist_factory(param)
|
func = arglist_factory(param)
|
||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
func.fget.__name__ = param # to allow passing as a prop # type: ignore
|
# to allow passing as a prop
|
||||||
cls.vars[param] = cls.computed_vars[param] = func.set_state(cls) # type: ignore
|
func._var_name = param
|
||||||
|
cls.vars[param] = cls.computed_vars[param] = func._var_set_state(cls) # type: ignore
|
||||||
setattr(cls, param, func)
|
setattr(cls, param, func)
|
||||||
|
|
||||||
def __getattribute__(self, name: str) -> Any:
|
def __getattribute__(self, name: str) -> Any:
|
||||||
@ -912,7 +915,7 @@ class State(Base, ABC, extra=pydantic.Extra.allow):
|
|||||||
return set(
|
return set(
|
||||||
cvar_name
|
cvar_name
|
||||||
for cvar_name, cvar in self.computed_vars.items()
|
for cvar_name, cvar in self.computed_vars.items()
|
||||||
if not cvar.cache
|
if not cvar._cache
|
||||||
)
|
)
|
||||||
|
|
||||||
def _mark_dirty_computed_vars(self) -> None:
|
def _mark_dirty_computed_vars(self) -> None:
|
||||||
|
@ -7,8 +7,8 @@ from reflex.event import EventChain
|
|||||||
from reflex.utils import format
|
from reflex.utils import format
|
||||||
from reflex.vars import BaseVar, Var
|
from reflex.vars import BaseVar, Var
|
||||||
|
|
||||||
color_mode = BaseVar(name=constants.ColorMode.NAME, type_="str")
|
color_mode = BaseVar(_var_name=constants.ColorMode.NAME, _var_type="str")
|
||||||
toggle_color_mode = BaseVar(name=constants.ColorMode.TOGGLE, type_=EventChain)
|
toggle_color_mode = BaseVar(_var_name=constants.ColorMode.TOGGLE, _var_type=EventChain)
|
||||||
|
|
||||||
|
|
||||||
def convert(style_dict):
|
def convert(style_dict):
|
||||||
|
@ -106,6 +106,7 @@ class AppHarness:
|
|||||||
app_instance: Optional[reflex.App] = None
|
app_instance: Optional[reflex.App] = None
|
||||||
frontend_process: Optional[subprocess.Popen] = None
|
frontend_process: Optional[subprocess.Popen] = None
|
||||||
frontend_url: Optional[str] = None
|
frontend_url: Optional[str] = None
|
||||||
|
frontend_output_thread: Optional[threading.Thread] = None
|
||||||
backend_thread: Optional[threading.Thread] = None
|
backend_thread: Optional[threading.Thread] = None
|
||||||
backend: Optional[uvicorn.Server] = None
|
backend: Optional[uvicorn.Server] = None
|
||||||
state_manager: Optional[StateManagerMemory | StateManagerRedis] = None
|
state_manager: Optional[StateManagerMemory | StateManagerRedis] = None
|
||||||
@ -230,6 +231,18 @@ class AppHarness:
|
|||||||
if self.frontend_url is None:
|
if self.frontend_url is None:
|
||||||
raise RuntimeError("Frontend did not start")
|
raise RuntimeError("Frontend did not start")
|
||||||
|
|
||||||
|
def consume_frontend_output():
|
||||||
|
while True:
|
||||||
|
line = (
|
||||||
|
self.frontend_process.stdout.readline() # pyright: ignore [reportOptionalMemberAccess]
|
||||||
|
)
|
||||||
|
if not line:
|
||||||
|
break
|
||||||
|
print(line)
|
||||||
|
|
||||||
|
self.frontend_output_thread = threading.Thread(target=consume_frontend_output)
|
||||||
|
self.frontend_output_thread.start()
|
||||||
|
|
||||||
def start(self) -> "AppHarness":
|
def start(self) -> "AppHarness":
|
||||||
"""Start the backend in a new thread and dev frontend as a separate process.
|
"""Start the backend in a new thread and dev frontend as a separate process.
|
||||||
|
|
||||||
@ -278,6 +291,8 @@ class AppHarness:
|
|||||||
self.frontend_process.communicate()
|
self.frontend_process.communicate()
|
||||||
if self.backend_thread is not None:
|
if self.backend_thread is not None:
|
||||||
self.backend_thread.join()
|
self.backend_thread.join()
|
||||||
|
if self.frontend_output_thread is not None:
|
||||||
|
self.frontend_output_thread.join()
|
||||||
for driver in self._frontends:
|
for driver in self._frontends:
|
||||||
driver.quit()
|
driver.quit()
|
||||||
|
|
||||||
|
@ -203,13 +203,13 @@ def format_var(var: Var) -> str:
|
|||||||
Returns:
|
Returns:
|
||||||
The formatted Var.
|
The formatted Var.
|
||||||
"""
|
"""
|
||||||
if not var.is_local or var.is_string:
|
if not var._var_is_local or var._var_is_string:
|
||||||
return str(var)
|
return str(var)
|
||||||
if types._issubclass(var.type_, str):
|
if types._issubclass(var._var_type, str):
|
||||||
return format_string(var.full_name)
|
return format_string(var._var_full_name)
|
||||||
if is_wrapped(var.full_name, "{"):
|
if is_wrapped(var._var_full_name, "{"):
|
||||||
return var.full_name
|
return var._var_full_name
|
||||||
return json_dumps(var.full_name)
|
return json_dumps(var._var_full_name)
|
||||||
|
|
||||||
|
|
||||||
def format_route(route: str, format_case=True) -> str:
|
def format_route(route: str, format_case=True) -> str:
|
||||||
@ -259,12 +259,16 @@ def format_cond(
|
|||||||
|
|
||||||
# Format prop conds.
|
# Format prop conds.
|
||||||
if is_prop:
|
if is_prop:
|
||||||
prop1 = Var.create_safe(true_value, is_string=type(true_value) is str).set(
|
prop1 = Var.create_safe(
|
||||||
is_local=True
|
true_value,
|
||||||
) # type: ignore
|
_var_is_string=type(true_value) is str,
|
||||||
prop2 = Var.create_safe(false_value, is_string=type(false_value) is str).set(
|
)
|
||||||
is_local=True
|
prop1._var_is_local = True
|
||||||
) # type: ignore
|
prop2 = Var.create_safe(
|
||||||
|
false_value,
|
||||||
|
_var_is_string=type(false_value) is str,
|
||||||
|
)
|
||||||
|
prop2._var_is_local = True
|
||||||
return f"{cond} ? {prop1} : {prop2}".replace("{", "").replace("}", "")
|
return f"{cond} ? {prop1} : {prop2}".replace("{", "").replace("}", "")
|
||||||
|
|
||||||
# Format component conds.
|
# Format component conds.
|
||||||
@ -292,11 +296,11 @@ def format_prop(
|
|||||||
try:
|
try:
|
||||||
# Handle var props.
|
# Handle var props.
|
||||||
if isinstance(prop, Var):
|
if isinstance(prop, Var):
|
||||||
if not prop.is_local or prop.is_string:
|
if not prop._var_is_local or prop._var_is_string:
|
||||||
return str(prop)
|
return str(prop)
|
||||||
if types._issubclass(prop.type_, str):
|
if types._issubclass(prop._var_type, str):
|
||||||
return format_string(prop.full_name)
|
return format_string(prop._var_full_name)
|
||||||
prop = prop.full_name
|
prop = prop._var_full_name
|
||||||
|
|
||||||
# Handle event props.
|
# Handle event props.
|
||||||
elif isinstance(prop, EventChain):
|
elif isinstance(prop, EventChain):
|
||||||
@ -414,7 +418,12 @@ def format_event(event_spec: EventSpec) -> str:
|
|||||||
args = ",".join(
|
args = ",".join(
|
||||||
[
|
[
|
||||||
":".join(
|
":".join(
|
||||||
(name.name, json.dumps(val.name) if val.is_string else val.full_name)
|
(
|
||||||
|
name._var_name,
|
||||||
|
json.dumps(val._var_name)
|
||||||
|
if val._var_is_string
|
||||||
|
else val._var_full_name,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
for name, val in event_spec.args
|
for name, val in event_spec.args
|
||||||
]
|
]
|
||||||
@ -448,7 +457,7 @@ def format_event_chain(
|
|||||||
if isinstance(event_chain, Var):
|
if isinstance(event_chain, Var):
|
||||||
from reflex.event import EventChain
|
from reflex.event import EventChain
|
||||||
|
|
||||||
if event_chain.type_ is not EventChain:
|
if event_chain._var_type is not EventChain:
|
||||||
raise ValueError(f"Invalid event chain: {event_chain}")
|
raise ValueError(f"Invalid event chain: {event_chain}")
|
||||||
return "".join(
|
return "".join(
|
||||||
[
|
[
|
||||||
@ -540,7 +549,7 @@ def format_array_ref(refs: str, idx: Var | None) -> str:
|
|||||||
"""
|
"""
|
||||||
clean_ref = re.sub(r"[^\w]+", "_", refs)
|
clean_ref = re.sub(r"[^\w]+", "_", refs)
|
||||||
if idx is not None:
|
if idx is not None:
|
||||||
idx.is_local = True
|
idx._var_is_local = True
|
||||||
return f"refs_{clean_ref}[{idx}]"
|
return f"refs_{clean_ref}[{idx}]"
|
||||||
return f"refs_{clean_ref}"
|
return f"refs_{clean_ref}"
|
||||||
|
|
||||||
|
481
reflex/vars.py
481
reflex/vars.py
@ -2,11 +2,12 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import contextlib
|
import contextlib
|
||||||
|
import dataclasses
|
||||||
import dis
|
import dis
|
||||||
import json
|
import json
|
||||||
import random
|
import random
|
||||||
import string
|
import string
|
||||||
from abc import ABC
|
import sys
|
||||||
from types import CodeType, FunctionType
|
from types import CodeType, FunctionType
|
||||||
from typing import (
|
from typing import (
|
||||||
TYPE_CHECKING,
|
TYPE_CHECKING,
|
||||||
@ -64,6 +65,18 @@ OPERATION_MAPPING = {
|
|||||||
(list, list): {"+", ">", "<", "<=", ">="},
|
(list, list): {"+", ">", "<", "<=", ">="},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# These names were changed in reflex 0.3.0
|
||||||
|
REPLACED_NAMES = {
|
||||||
|
"full_name": "_var_full_name",
|
||||||
|
"name": "_var_name",
|
||||||
|
"state": "_var_state",
|
||||||
|
"type_": "_var_type",
|
||||||
|
"is_local": "_var_is_local",
|
||||||
|
"is_string": "_var_is_string",
|
||||||
|
"set_state": "_var_set_state",
|
||||||
|
"deps": "_deps",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def get_unique_variable_name() -> str:
|
def get_unique_variable_name() -> str:
|
||||||
"""Get a unique variable name.
|
"""Get a unique variable name.
|
||||||
@ -78,34 +91,34 @@ def get_unique_variable_name() -> str:
|
|||||||
return get_unique_variable_name()
|
return get_unique_variable_name()
|
||||||
|
|
||||||
|
|
||||||
class Var(ABC):
|
class Var:
|
||||||
"""An abstract var."""
|
"""An abstract var."""
|
||||||
|
|
||||||
# The name of the var.
|
# The name of the var.
|
||||||
name: str
|
_var_name: str
|
||||||
|
|
||||||
# The type of the var.
|
# The type of the var.
|
||||||
type_: Type
|
_var_type: Type
|
||||||
|
|
||||||
# The name of the enclosing state.
|
# The name of the enclosing state.
|
||||||
state: str = ""
|
_var_state: str
|
||||||
|
|
||||||
# Whether this is a local javascript variable.
|
# Whether this is a local javascript variable.
|
||||||
is_local: bool = False
|
_var_is_local: bool
|
||||||
|
|
||||||
# Whether the var is a string literal.
|
# Whether the var is a string literal.
|
||||||
is_string: bool = False
|
_var_is_string: bool
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(
|
def create(
|
||||||
cls, value: Any, is_local: bool = True, is_string: bool = False
|
cls, value: Any, _var_is_local: bool = True, _var_is_string: bool = False
|
||||||
) -> Var | None:
|
) -> Var | None:
|
||||||
"""Create a var from a value.
|
"""Create a var from a value.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
value: The value to create the var from.
|
value: The value to create the var from.
|
||||||
is_local: Whether the var is local.
|
_var_is_local: Whether the var is local.
|
||||||
is_string: Whether the var is a string literal.
|
_var_is_string: Whether the var is a string literal.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The var.
|
The var.
|
||||||
@ -130,23 +143,32 @@ class Var(ABC):
|
|||||||
)
|
)
|
||||||
name = name if isinstance(name, str) else format.json_dumps(name)
|
name = name if isinstance(name, str) else format.json_dumps(name)
|
||||||
|
|
||||||
return BaseVar(name=name, type_=type_, is_local=is_local, is_string=is_string)
|
return BaseVar(
|
||||||
|
_var_name=name,
|
||||||
|
_var_type=type_,
|
||||||
|
_var_is_local=_var_is_local,
|
||||||
|
_var_is_string=_var_is_string,
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_safe(
|
def create_safe(
|
||||||
cls, value: Any, is_local: bool = True, is_string: bool = False
|
cls, value: Any, _var_is_local: bool = True, _var_is_string: bool = False
|
||||||
) -> Var:
|
) -> Var:
|
||||||
"""Create a var from a value, guaranteeing that it is not None.
|
"""Create a var from a value, asserting that it is not None.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
value: The value to create the var from.
|
value: The value to create the var from.
|
||||||
is_local: Whether the var is local.
|
_var_is_local: Whether the var is local.
|
||||||
is_string: Whether the var is a string literal.
|
_var_is_string: Whether the var is a string literal.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The var.
|
The var.
|
||||||
"""
|
"""
|
||||||
var = cls.create(value, is_local=is_local, is_string=is_string)
|
var = cls.create(
|
||||||
|
value,
|
||||||
|
_var_is_local=_var_is_local,
|
||||||
|
_var_is_string=_var_is_string,
|
||||||
|
)
|
||||||
assert var is not None
|
assert var is not None
|
||||||
return var
|
return var
|
||||||
|
|
||||||
@ -171,14 +193,14 @@ class Var(ABC):
|
|||||||
Returns:
|
Returns:
|
||||||
The decoded value or the Var name.
|
The decoded value or the Var name.
|
||||||
"""
|
"""
|
||||||
if self.state:
|
if self._var_state:
|
||||||
return self.full_name
|
return self._var_full_name
|
||||||
if self.is_string:
|
if self._var_is_string:
|
||||||
return self.name
|
return self._var_name
|
||||||
try:
|
try:
|
||||||
return json.loads(self.name)
|
return json.loads(self._var_name)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return self.name
|
return self._var_name
|
||||||
|
|
||||||
def equals(self, other: Var) -> bool:
|
def equals(self, other: Var) -> bool:
|
||||||
"""Check if two vars are equal.
|
"""Check if two vars are equal.
|
||||||
@ -190,10 +212,10 @@ class Var(ABC):
|
|||||||
Whether the vars are equal.
|
Whether the vars are equal.
|
||||||
"""
|
"""
|
||||||
return (
|
return (
|
||||||
self.name == other.name
|
self._var_name == other._var_name
|
||||||
and self.type_ == other.type_
|
and self._var_type == other._var_type
|
||||||
and self.state == other.state
|
and self._var_state == other._var_state
|
||||||
and self.is_local == other.is_local
|
and self._var_is_local == other._var_is_local
|
||||||
)
|
)
|
||||||
|
|
||||||
def to_string(self, json: bool = True) -> Var:
|
def to_string(self, json: bool = True) -> Var:
|
||||||
@ -214,7 +236,7 @@ class Var(ABC):
|
|||||||
Returns:
|
Returns:
|
||||||
The hash of the var.
|
The hash of the var.
|
||||||
"""
|
"""
|
||||||
return hash((self.name, str(self.type_)))
|
return hash((self._var_name, str(self._var_type)))
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
"""Wrap the var so it can be used in templates.
|
"""Wrap the var so it can be used in templates.
|
||||||
@ -222,8 +244,12 @@ class Var(ABC):
|
|||||||
Returns:
|
Returns:
|
||||||
The wrapped var, i.e. {state.var}.
|
The wrapped var, i.e. {state.var}.
|
||||||
"""
|
"""
|
||||||
out = self.full_name if self.is_local else format.wrap(self.full_name, "{")
|
out = (
|
||||||
if self.is_string:
|
self._var_full_name
|
||||||
|
if self._var_is_local
|
||||||
|
else format.wrap(self._var_full_name, "{")
|
||||||
|
)
|
||||||
|
if self._var_is_string:
|
||||||
out = format.format_string(out)
|
out = format.format_string(out)
|
||||||
return out
|
return out
|
||||||
|
|
||||||
@ -234,7 +260,7 @@ class Var(ABC):
|
|||||||
TypeError: when attempting to bool-ify the Var.
|
TypeError: when attempting to bool-ify the Var.
|
||||||
"""
|
"""
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
f"Cannot convert Var {self.full_name!r} to bool for use with `if`, `and`, `or`, and `not`. "
|
f"Cannot convert Var {self._var_full_name!r} to bool for use with `if`, `and`, `or`, and `not`. "
|
||||||
"Instead use `rx.cond` and bitwise operators `&` (and), `|` (or), `~` (invert)."
|
"Instead use `rx.cond` and bitwise operators `&` (and), `|` (or), `~` (invert)."
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -245,7 +271,7 @@ class Var(ABC):
|
|||||||
TypeError: when attempting to iterate over the Var.
|
TypeError: when attempting to iterate over the Var.
|
||||||
"""
|
"""
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
f"Cannot iterate over Var {self.full_name!r}. Instead use `rx.foreach`."
|
f"Cannot iterate over Var {self._var_full_name!r}. Instead use `rx.foreach`."
|
||||||
)
|
)
|
||||||
|
|
||||||
def __format__(self, format_spec: str) -> str:
|
def __format__(self, format_spec: str) -> str:
|
||||||
@ -257,7 +283,7 @@ class Var(ABC):
|
|||||||
Returns:
|
Returns:
|
||||||
The formatted var.
|
The formatted var.
|
||||||
"""
|
"""
|
||||||
if self.is_local:
|
if self._var_is_local:
|
||||||
return str(self)
|
return str(self)
|
||||||
return f"${str(self)}"
|
return f"${str(self)}"
|
||||||
|
|
||||||
@ -275,16 +301,16 @@ class Var(ABC):
|
|||||||
"""
|
"""
|
||||||
# Indexing is only supported for strings, lists, tuples, dicts, and dataframes.
|
# Indexing is only supported for strings, lists, tuples, dicts, and dataframes.
|
||||||
if not (
|
if not (
|
||||||
types._issubclass(self.type_, Union[List, Dict, Tuple, str])
|
types._issubclass(self._var_type, Union[List, Dict, Tuple, str])
|
||||||
or types.is_dataframe(self.type_)
|
or types.is_dataframe(self._var_type)
|
||||||
):
|
):
|
||||||
if self.type_ == Any:
|
if self._var_type == Any:
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
"Could not index into var of type Any. (If you are trying to index into a state var, "
|
"Could not index into var of type Any. (If you are trying to index into a state var, "
|
||||||
"add the correct type annotation to the var.)"
|
"add the correct type annotation to the var.)"
|
||||||
)
|
)
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
f"Var {self.name} of type {self.type_} does not support indexing."
|
f"Var {self._var_name} of type {self._var_type} does not support indexing."
|
||||||
)
|
)
|
||||||
|
|
||||||
# The type of the indexed var.
|
# The type of the indexed var.
|
||||||
@ -292,15 +318,20 @@ class Var(ABC):
|
|||||||
|
|
||||||
# Convert any vars to local vars.
|
# Convert any vars to local vars.
|
||||||
if isinstance(i, Var):
|
if isinstance(i, Var):
|
||||||
i = BaseVar(name=i.name, type_=i.type_, state=i.state, is_local=True)
|
i = BaseVar(
|
||||||
|
_var_name=i._var_name,
|
||||||
|
_var_type=i._var_type,
|
||||||
|
_var_state=i._var_state,
|
||||||
|
_var_is_local=True,
|
||||||
|
)
|
||||||
|
|
||||||
# Handle list/tuple/str indexing.
|
# Handle list/tuple/str indexing.
|
||||||
if types._issubclass(self.type_, Union[List, Tuple, str]):
|
if types._issubclass(self._var_type, Union[List, Tuple, str]):
|
||||||
# List/Tuple/String indices must be ints, slices, or vars.
|
# List/Tuple/String indices must be ints, slices, or vars.
|
||||||
if (
|
if (
|
||||||
not isinstance(i, types.get_args(Union[int, slice, Var]))
|
not isinstance(i, types.get_args(Union[int, slice, Var]))
|
||||||
or isinstance(i, Var)
|
or isinstance(i, Var)
|
||||||
and not i.type_ == int
|
and not i._var_type == int
|
||||||
):
|
):
|
||||||
raise TypeError("Index must be an integer or an integer var.")
|
raise TypeError("Index must be an integer or an integer var.")
|
||||||
|
|
||||||
@ -312,35 +343,40 @@ class Var(ABC):
|
|||||||
|
|
||||||
# Use the slice function.
|
# Use the slice function.
|
||||||
return BaseVar(
|
return BaseVar(
|
||||||
name=f"{self.name}.slice({start}, {stop})",
|
_var_name=f"{self._var_name}.slice({start}, {stop})",
|
||||||
type_=self.type_,
|
_var_type=self._var_type,
|
||||||
state=self.state,
|
_var_state=self._var_state,
|
||||||
is_local=self.is_local,
|
_var_is_local=self._var_is_local,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Get the type of the indexed var.
|
# Get the type of the indexed var.
|
||||||
type_ = (
|
type_ = (
|
||||||
types.get_args(self.type_)[0]
|
types.get_args(self._var_type)[0]
|
||||||
if types.is_generic_alias(self.type_)
|
if types.is_generic_alias(self._var_type)
|
||||||
else Any
|
else Any
|
||||||
)
|
)
|
||||||
|
|
||||||
# Use `at` to support negative indices.
|
# Use `at` to support negative indices.
|
||||||
return BaseVar(
|
return BaseVar(
|
||||||
name=f"{self.name}.at({i})",
|
_var_name=f"{self._var_name}.at({i})",
|
||||||
type_=type_,
|
_var_type=type_,
|
||||||
state=self.state,
|
_var_state=self._var_state,
|
||||||
is_local=self.is_local,
|
_var_is_local=self._var_is_local,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Dictionary / dataframe indexing.
|
# Dictionary / dataframe indexing.
|
||||||
# Tuples are currently not supported as indexes.
|
# Tuples are currently not supported as indexes.
|
||||||
if (
|
if (
|
||||||
(types._issubclass(self.type_, Dict) or types.is_dataframe(self.type_))
|
(
|
||||||
|
types._issubclass(self._var_type, Dict)
|
||||||
|
or types.is_dataframe(self._var_type)
|
||||||
|
)
|
||||||
and not isinstance(i, types.get_args(Union[int, str, float, Var]))
|
and not isinstance(i, types.get_args(Union[int, str, float, Var]))
|
||||||
) or (
|
) or (
|
||||||
isinstance(i, Var)
|
isinstance(i, Var)
|
||||||
and not types._issubclass(i.type_, types.get_args(Union[int, str, float]))
|
and not types._issubclass(
|
||||||
|
i._var_type, types.get_args(Union[int, str, float])
|
||||||
|
)
|
||||||
):
|
):
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
"Index must be one of the following types: int, str, int or str Var"
|
"Index must be one of the following types: int, str, int or str Var"
|
||||||
@ -349,18 +385,20 @@ class Var(ABC):
|
|||||||
if isinstance(i, str):
|
if isinstance(i, str):
|
||||||
i = format.wrap(i, '"')
|
i = format.wrap(i, '"')
|
||||||
type_ = (
|
type_ = (
|
||||||
types.get_args(self.type_)[1] if types.is_generic_alias(self.type_) else Any
|
types.get_args(self._var_type)[1]
|
||||||
|
if types.is_generic_alias(self._var_type)
|
||||||
|
else Any
|
||||||
)
|
)
|
||||||
|
|
||||||
# Use normal indexing here.
|
# Use normal indexing here.
|
||||||
return BaseVar(
|
return BaseVar(
|
||||||
name=f"{self.name}[{i}]",
|
_var_name=f"{self._var_name}[{i}]",
|
||||||
type_=type_,
|
_var_type=type_,
|
||||||
state=self.state,
|
_var_state=self._var_state,
|
||||||
is_local=self.is_local,
|
_var_is_local=self._var_is_local,
|
||||||
)
|
)
|
||||||
|
|
||||||
def __getattribute__(self, name: str) -> Var:
|
def __getattr__(self, name: str) -> Var:
|
||||||
"""Get a var attribute.
|
"""Get a var attribute.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -373,30 +411,39 @@ class Var(ABC):
|
|||||||
AttributeError: If the var is wrongly annotated or can't find attribute.
|
AttributeError: If the var is wrongly annotated or can't find attribute.
|
||||||
TypeError: If an annotation to the var isn't provided.
|
TypeError: If an annotation to the var isn't provided.
|
||||||
"""
|
"""
|
||||||
try:
|
# Check if the attribute is one of the class fields.
|
||||||
return super().__getattribute__(name)
|
if not name.startswith("_"):
|
||||||
except Exception as e:
|
if self._var_type == Any:
|
||||||
# Check if the attribute is one of the class fields.
|
raise TypeError(
|
||||||
if not name.startswith("_"):
|
f"You must provide an annotation for the state var `{self._var_full_name}`. Annotation cannot be `{self._var_type}`"
|
||||||
if self.type_ == Any:
|
) from None
|
||||||
raise TypeError(
|
if (
|
||||||
f"You must provide an annotation for the state var `{self.full_name}`. Annotation cannot be `{self.type_}`"
|
hasattr(self._var_type, "__fields__")
|
||||||
) from None
|
and name in self._var_type.__fields__
|
||||||
if hasattr(self.type_, "__fields__") and name in self.type_.__fields__:
|
):
|
||||||
type_ = self.type_.__fields__[name].outer_type_
|
type_ = self._var_type.__fields__[name].outer_type_
|
||||||
if isinstance(type_, ModelField):
|
if isinstance(type_, ModelField):
|
||||||
type_ = type_.type_
|
type_ = type_.type_
|
||||||
return BaseVar(
|
return BaseVar(
|
||||||
name=f"{self.name}.{name}",
|
_var_name=f"{self._var_name}.{name}",
|
||||||
type_=type_,
|
_var_type=type_,
|
||||||
state=self.state,
|
_var_state=self._var_state,
|
||||||
is_local=self.is_local,
|
_var_is_local=self._var_is_local,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if name in REPLACED_NAMES:
|
||||||
|
raise AttributeError(
|
||||||
|
f"Field {name!r} was renamed to {REPLACED_NAMES[name]!r}"
|
||||||
|
)
|
||||||
|
|
||||||
raise AttributeError(
|
raise AttributeError(
|
||||||
f"The State var `{self.full_name}` has no attribute '{name}' or may have been annotated "
|
f"The State var `{self._var_full_name}` has no attribute '{name}' or may have been annotated "
|
||||||
f"wrongly.\n"
|
f"wrongly."
|
||||||
f"original message: {e.args[0]}"
|
)
|
||||||
) from e
|
|
||||||
|
raise AttributeError(
|
||||||
|
f"The State var has no attribute '{name}' or may have been annotated wrongly.",
|
||||||
|
)
|
||||||
|
|
||||||
def operation(
|
def operation(
|
||||||
self,
|
self,
|
||||||
@ -429,7 +476,7 @@ class Var(ABC):
|
|||||||
else:
|
else:
|
||||||
other = Var.create(other)
|
other = Var.create(other)
|
||||||
|
|
||||||
type_ = type_ or self.type_
|
type_ = type_ or self._var_type
|
||||||
|
|
||||||
if other is None and flip:
|
if other is None and flip:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
@ -441,42 +488,42 @@ class Var(ABC):
|
|||||||
if other is not None:
|
if other is not None:
|
||||||
# check if the operation between operands is valid.
|
# check if the operation between operands is valid.
|
||||||
if op and not self.is_valid_operation(
|
if op and not self.is_valid_operation(
|
||||||
types.get_base_class(left_operand.type_), # type: ignore
|
types.get_base_class(left_operand._var_type), # type: ignore
|
||||||
types.get_base_class(right_operand.type_), # type: ignore
|
types.get_base_class(right_operand._var_type), # type: ignore
|
||||||
op,
|
op,
|
||||||
):
|
):
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
f"Unsupported Operand type(s) for {op}: `{left_operand.full_name}` of type {left_operand.type_.__name__} and `{right_operand.full_name}` of type {right_operand.type_.__name__}" # type: ignore
|
f"Unsupported Operand type(s) for {op}: `{left_operand._var_full_name}` of type {left_operand._var_type.__name__} and `{right_operand._var_full_name}` of type {right_operand._var_type.__name__}" # type: ignore
|
||||||
)
|
)
|
||||||
|
|
||||||
# apply function to operands
|
# apply function to operands
|
||||||
if fn is not None:
|
if fn is not None:
|
||||||
if invoke_fn:
|
if invoke_fn:
|
||||||
# invoke the function on left operand.
|
# invoke the function on left operand.
|
||||||
operation_name = f"{left_operand.full_name}.{fn}({right_operand.full_name})" # type: ignore
|
operation_name = f"{left_operand._var_full_name}.{fn}({right_operand._var_full_name})" # type: ignore
|
||||||
else:
|
else:
|
||||||
# pass the operands as arguments to the function.
|
# pass the operands as arguments to the function.
|
||||||
operation_name = f"{left_operand.full_name} {op} {right_operand.full_name}" # type: ignore
|
operation_name = f"{left_operand._var_full_name} {op} {right_operand._var_full_name}" # type: ignore
|
||||||
operation_name = f"{fn}({operation_name})"
|
operation_name = f"{fn}({operation_name})"
|
||||||
else:
|
else:
|
||||||
# apply operator to operands (left operand <operator> right_operand)
|
# apply operator to operands (left operand <operator> right_operand)
|
||||||
operation_name = f"{left_operand.full_name} {op} {right_operand.full_name}" # type: ignore
|
operation_name = f"{left_operand._var_full_name} {op} {right_operand._var_full_name}" # type: ignore
|
||||||
operation_name = format.wrap(operation_name, "(")
|
operation_name = format.wrap(operation_name, "(")
|
||||||
else:
|
else:
|
||||||
# apply operator to left operand (<operator> left_operand)
|
# apply operator to left operand (<operator> left_operand)
|
||||||
operation_name = f"{op}{self.full_name}"
|
operation_name = f"{op}{self._var_full_name}"
|
||||||
# apply function to operands
|
# apply function to operands
|
||||||
if fn is not None:
|
if fn is not None:
|
||||||
operation_name = (
|
operation_name = (
|
||||||
f"{fn}({operation_name})"
|
f"{fn}({operation_name})"
|
||||||
if not invoke_fn
|
if not invoke_fn
|
||||||
else f"{self.full_name}.{fn}()"
|
else f"{self._var_full_name}.{fn}()"
|
||||||
)
|
)
|
||||||
|
|
||||||
return BaseVar(
|
return BaseVar(
|
||||||
name=operation_name,
|
_var_name=operation_name,
|
||||||
type_=type_,
|
_var_type=type_,
|
||||||
is_local=self.is_local,
|
_var_is_local=self._var_is_local,
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -554,12 +601,12 @@ class Var(ABC):
|
|||||||
Raises:
|
Raises:
|
||||||
TypeError: If the var is not a list.
|
TypeError: If the var is not a list.
|
||||||
"""
|
"""
|
||||||
if not types._issubclass(self.type_, List):
|
if not types._issubclass(self._var_type, List):
|
||||||
raise TypeError(f"Cannot get length of non-list var {self}.")
|
raise TypeError(f"Cannot get length of non-list var {self}.")
|
||||||
return BaseVar(
|
return BaseVar(
|
||||||
name=f"{self.full_name}.length",
|
_var_name=f"{self._var_full_name}.length",
|
||||||
type_=int,
|
_var_type=int,
|
||||||
is_local=self.is_local,
|
_var_is_local=self._var_is_local,
|
||||||
)
|
)
|
||||||
|
|
||||||
def __eq__(self, other: Var) -> Var:
|
def __eq__(self, other: Var) -> Var:
|
||||||
@ -638,12 +685,12 @@ class Var(ABC):
|
|||||||
Returns:
|
Returns:
|
||||||
A var representing the sum.
|
A var representing the sum.
|
||||||
"""
|
"""
|
||||||
other_type = other.type_ if isinstance(other, Var) else type(other)
|
other_type = other._var_type if isinstance(other, Var) else type(other)
|
||||||
# For list-list addition, javascript concatenates the content of the lists instead of
|
# For list-list addition, javascript concatenates the content of the lists instead of
|
||||||
# merging the list, and for that reason we use the spread operator available through spreadArraysOrObjects
|
# merging the list, and for that reason we use the spread operator available through spreadArraysOrObjects
|
||||||
# utility function
|
# utility function
|
||||||
if (
|
if (
|
||||||
types.get_base_class(self.type_) == list
|
types.get_base_class(self._var_type) == list
|
||||||
and types.get_base_class(other_type) == list
|
and types.get_base_class(other_type) == list
|
||||||
):
|
):
|
||||||
return self.operation(",", other, fn="spreadArraysOrObjects", flip=flip)
|
return self.operation(",", other, fn="spreadArraysOrObjects", flip=flip)
|
||||||
@ -692,10 +739,10 @@ class Var(ABC):
|
|||||||
Returns:
|
Returns:
|
||||||
A var representing the product.
|
A var representing the product.
|
||||||
"""
|
"""
|
||||||
other_type = other.type_ if isinstance(other, Var) else type(other)
|
other_type = other._var_type if isinstance(other, Var) else type(other)
|
||||||
# For str-int multiplication, we use the repeat function.
|
# For str-int multiplication, we use the repeat function.
|
||||||
# i.e "hello" * 2 is equivalent to "hello".repeat(2) in js.
|
# i.e "hello" * 2 is equivalent to "hello".repeat(2) in js.
|
||||||
if (types.get_base_class(self.type_), types.get_base_class(other_type)) in [
|
if (types.get_base_class(self._var_type), types.get_base_class(other_type)) in [
|
||||||
(int, str),
|
(int, str),
|
||||||
(str, int),
|
(str, int),
|
||||||
]:
|
]:
|
||||||
@ -703,16 +750,16 @@ class Var(ABC):
|
|||||||
|
|
||||||
# For list-int multiplication, we use the Array function.
|
# For list-int multiplication, we use the Array function.
|
||||||
# i.e ["hello"] * 2 is equivalent to Array(2).fill().map(() => ["hello"]).flat() in js.
|
# i.e ["hello"] * 2 is equivalent to Array(2).fill().map(() => ["hello"]).flat() in js.
|
||||||
if (types.get_base_class(self.type_), types.get_base_class(other_type)) in [
|
if (types.get_base_class(self._var_type), types.get_base_class(other_type)) in [
|
||||||
(int, list),
|
(int, list),
|
||||||
(list, int),
|
(list, int),
|
||||||
]:
|
]:
|
||||||
other_name = other.full_name if isinstance(other, Var) else other
|
other_name = other._var_full_name if isinstance(other, Var) else other
|
||||||
name = f"Array({other_name}).fill().map(() => {self.full_name}).flat()"
|
name = f"Array({other_name}).fill().map(() => {self._var_full_name}).flat()"
|
||||||
return BaseVar(
|
return BaseVar(
|
||||||
name=name,
|
_var_name=name,
|
||||||
type_=str,
|
_var_type=str,
|
||||||
is_local=self.is_local,
|
_var_is_local=self._var_is_local,
|
||||||
)
|
)
|
||||||
|
|
||||||
return self.operation("*", other)
|
return self.operation("*", other)
|
||||||
@ -830,7 +877,7 @@ class Var(ABC):
|
|||||||
>>> var1 = Var.create(True)
|
>>> var1 = Var.create(True)
|
||||||
>>> var2 = Var.create(False)
|
>>> var2 = Var.create(False)
|
||||||
>>> js_code = var1 & var2
|
>>> js_code = var1 & var2
|
||||||
>>> print(js_code.full_name)
|
>>> print(js_code._var_full_name)
|
||||||
'(true && false)'
|
'(true && false)'
|
||||||
"""
|
"""
|
||||||
return self.operation("&&", other, type_=bool)
|
return self.operation("&&", other, type_=bool)
|
||||||
@ -860,7 +907,7 @@ class Var(ABC):
|
|||||||
>>> var1 = Var.create(True)
|
>>> var1 = Var.create(True)
|
||||||
>>> var2 = Var.create(False)
|
>>> var2 = Var.create(False)
|
||||||
>>> js_code = var1 & var2
|
>>> js_code = var1 & var2
|
||||||
>>> print(js_code.full_name)
|
>>> print(js_code._var_full_name)
|
||||||
'(false && true)'
|
'(false && true)'
|
||||||
"""
|
"""
|
||||||
return self.operation("&&", other, type_=bool, flip=True)
|
return self.operation("&&", other, type_=bool, flip=True)
|
||||||
@ -888,7 +935,7 @@ class Var(ABC):
|
|||||||
>>> var1 = Var.create(True)
|
>>> var1 = Var.create(True)
|
||||||
>>> var2 = Var.create(False)
|
>>> var2 = Var.create(False)
|
||||||
>>> js_code = var1 | var2
|
>>> js_code = var1 | var2
|
||||||
>>> print(js_code.full_name)
|
>>> print(js_code._var_full_name)
|
||||||
'(true || false)'
|
'(true || false)'
|
||||||
"""
|
"""
|
||||||
return self.operation("||", other, type_=bool)
|
return self.operation("||", other, type_=bool)
|
||||||
@ -943,35 +990,37 @@ class Var(ABC):
|
|||||||
Returns:
|
Returns:
|
||||||
A var representing the contain check.
|
A var representing the contain check.
|
||||||
"""
|
"""
|
||||||
if not (types._issubclass(self.type_, Union[dict, list, tuple, str])):
|
if not (types._issubclass(self._var_type, Union[dict, list, tuple, str])):
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
f"Var {self.full_name} of type {self.type_} does not support contains check."
|
f"Var {self._var_full_name} of type {self._var_type} does not support contains check."
|
||||||
)
|
)
|
||||||
method = (
|
method = (
|
||||||
"hasOwnProperty" if types.get_base_class(self.type_) == dict else "includes"
|
"hasOwnProperty"
|
||||||
|
if types.get_base_class(self._var_type) == dict
|
||||||
|
else "includes"
|
||||||
)
|
)
|
||||||
if isinstance(other, str):
|
if isinstance(other, str):
|
||||||
other = Var.create(json.dumps(other), is_string=True)
|
other = Var.create(json.dumps(other), _var_is_string=True)
|
||||||
elif not isinstance(other, Var):
|
elif not isinstance(other, Var):
|
||||||
other = Var.create(other)
|
other = Var.create(other)
|
||||||
if types._issubclass(self.type_, Dict):
|
if types._issubclass(self._var_type, Dict):
|
||||||
return BaseVar(
|
return BaseVar(
|
||||||
name=f"{self.full_name}.{method}({other.full_name})",
|
_var_name=f"{self._var_full_name}.{method}({other._var_full_name})",
|
||||||
type_=bool,
|
_var_type=bool,
|
||||||
is_local=self.is_local,
|
_var_is_local=self._var_is_local,
|
||||||
)
|
)
|
||||||
else: # str, list, tuple
|
else: # str, list, tuple
|
||||||
# For strings, the left operand must be a string.
|
# For strings, the left operand must be a string.
|
||||||
if types._issubclass(self.type_, str) and not types._issubclass(
|
if types._issubclass(self._var_type, str) and not types._issubclass(
|
||||||
other.type_, str
|
other._var_type, str
|
||||||
):
|
):
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
f"'in <string>' requires string as left operand, not {other.type_}"
|
f"'in <string>' requires string as left operand, not {other._var_type}"
|
||||||
)
|
)
|
||||||
return BaseVar(
|
return BaseVar(
|
||||||
name=f"{self.full_name}.includes({other.full_name})",
|
_var_name=f"{self._var_full_name}.includes({other._var_full_name})",
|
||||||
type_=bool,
|
_var_type=bool,
|
||||||
is_local=self.is_local,
|
_var_is_local=self._var_is_local,
|
||||||
)
|
)
|
||||||
|
|
||||||
def reverse(self) -> Var:
|
def reverse(self) -> Var:
|
||||||
@ -983,13 +1032,13 @@ class Var(ABC):
|
|||||||
Returns:
|
Returns:
|
||||||
A var with the reversed list.
|
A var with the reversed list.
|
||||||
"""
|
"""
|
||||||
if not types._issubclass(self.type_, list):
|
if not types._issubclass(self._var_type, list):
|
||||||
raise TypeError(f"Cannot reverse non-list var {self.full_name}.")
|
raise TypeError(f"Cannot reverse non-list var {self._var_full_name}.")
|
||||||
|
|
||||||
return BaseVar(
|
return BaseVar(
|
||||||
name=f"[...{self.full_name}].reverse()",
|
_var_name=f"[...{self._var_full_name}].reverse()",
|
||||||
type_=self.type_,
|
_var_type=self._var_type,
|
||||||
is_local=self.is_local,
|
_var_is_local=self._var_is_local,
|
||||||
)
|
)
|
||||||
|
|
||||||
def lower(self) -> Var:
|
def lower(self) -> Var:
|
||||||
@ -1001,15 +1050,15 @@ class Var(ABC):
|
|||||||
Raises:
|
Raises:
|
||||||
TypeError: If the var is not a string.
|
TypeError: If the var is not a string.
|
||||||
"""
|
"""
|
||||||
if not types._issubclass(self.type_, str):
|
if not types._issubclass(self._var_type, str):
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
f"Cannot convert non-string var {self.full_name} to lowercase."
|
f"Cannot convert non-string var {self._var_full_name} to lowercase."
|
||||||
)
|
)
|
||||||
|
|
||||||
return BaseVar(
|
return BaseVar(
|
||||||
name=f"{self.full_name}.toLowerCase()",
|
_var_name=f"{self._var_full_name}.toLowerCase()",
|
||||||
type_=str,
|
_var_type=str,
|
||||||
is_local=self.is_local,
|
_var_is_local=self._var_is_local,
|
||||||
)
|
)
|
||||||
|
|
||||||
def upper(self) -> Var:
|
def upper(self) -> Var:
|
||||||
@ -1021,15 +1070,15 @@ class Var(ABC):
|
|||||||
Raises:
|
Raises:
|
||||||
TypeError: If the var is not a string.
|
TypeError: If the var is not a string.
|
||||||
"""
|
"""
|
||||||
if not types._issubclass(self.type_, str):
|
if not types._issubclass(self._var_type, str):
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
f"Cannot convert non-string var {self.full_name} to uppercase."
|
f"Cannot convert non-string var {self._var_full_name} to uppercase."
|
||||||
)
|
)
|
||||||
|
|
||||||
return BaseVar(
|
return BaseVar(
|
||||||
name=f"{self.full_name}.toUpperCase()",
|
_var_name=f"{self._var_full_name}.toUpperCase()",
|
||||||
type_=str,
|
_var_type=str,
|
||||||
is_local=self.is_local,
|
_var_is_local=self._var_is_local,
|
||||||
)
|
)
|
||||||
|
|
||||||
def split(self, other: str | Var[str] = " ") -> Var:
|
def split(self, other: str | Var[str] = " ") -> Var:
|
||||||
@ -1044,15 +1093,15 @@ class Var(ABC):
|
|||||||
Raises:
|
Raises:
|
||||||
TypeError: If the var is not a string.
|
TypeError: If the var is not a string.
|
||||||
"""
|
"""
|
||||||
if not types._issubclass(self.type_, str):
|
if not types._issubclass(self._var_type, str):
|
||||||
raise TypeError(f"Cannot split non-string var {self.full_name}.")
|
raise TypeError(f"Cannot split non-string var {self._var_full_name}.")
|
||||||
|
|
||||||
other = Var.create_safe(json.dumps(other)) if isinstance(other, str) else other
|
other = Var.create_safe(json.dumps(other)) if isinstance(other, str) else other
|
||||||
|
|
||||||
return BaseVar(
|
return BaseVar(
|
||||||
name=f"{self.full_name}.split({other.full_name})",
|
_var_name=f"{self._var_full_name}.split({other._var_full_name})",
|
||||||
type_=list[str],
|
_var_type=list[str],
|
||||||
is_local=self.is_local,
|
_var_is_local=self._var_is_local,
|
||||||
)
|
)
|
||||||
|
|
||||||
def join(self, other: str | Var[str] | None = None) -> Var:
|
def join(self, other: str | Var[str] | None = None) -> Var:
|
||||||
@ -1067,8 +1116,8 @@ class Var(ABC):
|
|||||||
Raises:
|
Raises:
|
||||||
TypeError: If the var is not a list.
|
TypeError: If the var is not a list.
|
||||||
"""
|
"""
|
||||||
if not types._issubclass(self.type_, list):
|
if not types._issubclass(self._var_type, list):
|
||||||
raise TypeError(f"Cannot join non-list var {self.full_name}.")
|
raise TypeError(f"Cannot join non-list var {self._var_full_name}.")
|
||||||
|
|
||||||
if other is None:
|
if other is None:
|
||||||
other = Var.create_safe("")
|
other = Var.create_safe("")
|
||||||
@ -1078,9 +1127,9 @@ class Var(ABC):
|
|||||||
other = Var.create_safe(other)
|
other = Var.create_safe(other)
|
||||||
|
|
||||||
return BaseVar(
|
return BaseVar(
|
||||||
name=f"{self.full_name}.join({other.full_name})",
|
_var_name=f"{self._var_full_name}.join({other._var_full_name})",
|
||||||
type_=str,
|
_var_type=str,
|
||||||
is_local=self.is_local,
|
_var_is_local=self._var_is_local,
|
||||||
)
|
)
|
||||||
|
|
||||||
def foreach(self, fn: Callable) -> Var:
|
def foreach(self, fn: Callable) -> Var:
|
||||||
@ -1093,13 +1142,13 @@ class Var(ABC):
|
|||||||
A var representing foreach operation.
|
A var representing foreach operation.
|
||||||
"""
|
"""
|
||||||
arg = BaseVar(
|
arg = BaseVar(
|
||||||
name=get_unique_variable_name(),
|
_var_name=get_unique_variable_name(),
|
||||||
type_=self.type_,
|
_var_type=self._var_type,
|
||||||
)
|
)
|
||||||
return BaseVar(
|
return BaseVar(
|
||||||
name=f"{self.full_name}.map(({arg.name}, i) => {fn(arg, key='i')})",
|
_var_name=f"{self._var_full_name}.map(({arg._var_name}, i) => {fn(arg, key='i')})",
|
||||||
type_=self.type_,
|
_var_type=self._var_type,
|
||||||
is_local=self.is_local,
|
_var_is_local=self._var_is_local,
|
||||||
)
|
)
|
||||||
|
|
||||||
def to(self, type_: Type) -> Var:
|
def to(self, type_: Type) -> Var:
|
||||||
@ -1112,22 +1161,26 @@ class Var(ABC):
|
|||||||
The converted var.
|
The converted var.
|
||||||
"""
|
"""
|
||||||
return BaseVar(
|
return BaseVar(
|
||||||
name=self.name,
|
_var_name=self._var_name,
|
||||||
type_=type_,
|
_var_type=type_,
|
||||||
state=self.state,
|
_var_state=self._var_state,
|
||||||
is_local=self.is_local,
|
_var_is_local=self._var_is_local,
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def full_name(self) -> str:
|
def _var_full_name(self) -> str:
|
||||||
"""Get the full name of the var.
|
"""Get the full name of the var.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The full name of the var.
|
The full name of the var.
|
||||||
"""
|
"""
|
||||||
return self.name if self.state == "" else ".".join([self.state, self.name])
|
return (
|
||||||
|
self._var_name
|
||||||
|
if self._var_state == ""
|
||||||
|
else ".".join([self._var_state, self._var_name])
|
||||||
|
)
|
||||||
|
|
||||||
def set_state(self, state: Type[State]) -> Any:
|
def _var_set_state(self, state: Type[State]) -> Any:
|
||||||
"""Set the state of the var.
|
"""Set the state of the var.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -1136,27 +1189,31 @@ class Var(ABC):
|
|||||||
Returns:
|
Returns:
|
||||||
The var with the set state.
|
The var with the set state.
|
||||||
"""
|
"""
|
||||||
self.state = state.get_full_name()
|
self._var_state = state.get_full_name()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
class BaseVar(Var, Base):
|
@dataclasses.dataclass(
|
||||||
|
eq=False,
|
||||||
|
**{"slots": True} if sys.version_info >= (3, 10) else {},
|
||||||
|
)
|
||||||
|
class BaseVar(Var):
|
||||||
"""A base (non-computed) var of the app state."""
|
"""A base (non-computed) var of the app state."""
|
||||||
|
|
||||||
# The name of the var.
|
# The name of the var.
|
||||||
name: str
|
_var_name: str = dataclasses.field()
|
||||||
|
|
||||||
# The type of the var.
|
# The type of the var.
|
||||||
type_: Any
|
_var_type: Type = dataclasses.field(default=Any)
|
||||||
|
|
||||||
# The name of the enclosing state.
|
# The name of the enclosing state.
|
||||||
state: str = ""
|
_var_state: str = dataclasses.field(default="")
|
||||||
|
|
||||||
# Whether this is a local javascript variable.
|
# Whether this is a local javascript variable.
|
||||||
is_local: bool = False
|
_var_is_local: bool = dataclasses.field(default=False)
|
||||||
|
|
||||||
# Whether this var is a raw string.
|
# Whether the var is a string literal.
|
||||||
is_string: bool = False
|
_var_is_string: bool = dataclasses.field(default=False)
|
||||||
|
|
||||||
def __hash__(self) -> int:
|
def __hash__(self) -> int:
|
||||||
"""Define a hash function for a var.
|
"""Define a hash function for a var.
|
||||||
@ -1164,7 +1221,7 @@ class BaseVar(Var, Base):
|
|||||||
Returns:
|
Returns:
|
||||||
The hash of the var.
|
The hash of the var.
|
||||||
"""
|
"""
|
||||||
return hash((self.name, str(self.type_)))
|
return hash((self._var_name, str(self._var_type)))
|
||||||
|
|
||||||
def get_default_value(self) -> Any:
|
def get_default_value(self) -> Any:
|
||||||
"""Get the default value of the var.
|
"""Get the default value of the var.
|
||||||
@ -1176,7 +1233,9 @@ class BaseVar(Var, Base):
|
|||||||
ImportError: If the var is a dataframe and pandas is not installed.
|
ImportError: If the var is a dataframe and pandas is not installed.
|
||||||
"""
|
"""
|
||||||
type_ = (
|
type_ = (
|
||||||
self.type_.__origin__ if types.is_generic_alias(self.type_) else self.type_
|
self._var_type.__origin__
|
||||||
|
if types.is_generic_alias(self._var_type)
|
||||||
|
else self._var_type
|
||||||
)
|
)
|
||||||
if issubclass(type_, str):
|
if issubclass(type_, str):
|
||||||
return ""
|
return ""
|
||||||
@ -1210,10 +1269,10 @@ class BaseVar(Var, Base):
|
|||||||
Returns:
|
Returns:
|
||||||
The name of the setter function.
|
The name of the setter function.
|
||||||
"""
|
"""
|
||||||
setter = constants.SETTER_PREFIX + self.name
|
setter = constants.SETTER_PREFIX + self._var_name
|
||||||
if not include_state or self.state == "":
|
if not include_state or self._var_state == "":
|
||||||
return setter
|
return setter
|
||||||
return ".".join((self.state, setter))
|
return ".".join((self._var_state, setter))
|
||||||
|
|
||||||
def get_setter(self) -> Callable[[State, Any], None]:
|
def get_setter(self) -> Callable[[State, Any], None]:
|
||||||
"""Get the var's setter function.
|
"""Get the var's setter function.
|
||||||
@ -1229,46 +1288,59 @@ class BaseVar(Var, Base):
|
|||||||
state: The state within which we add the setter function.
|
state: The state within which we add the setter function.
|
||||||
value: The value to set.
|
value: The value to set.
|
||||||
"""
|
"""
|
||||||
if self.type_ in [int, float]:
|
if self._var_type in [int, float]:
|
||||||
try:
|
try:
|
||||||
value = self.type_(value)
|
value = self._var_type(value)
|
||||||
setattr(state, self.name, value)
|
setattr(state, self._var_name, value)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
console.warn(
|
console.warn(
|
||||||
f"{self.name}: Failed conversion of {value} to '{self.type_.__name__}'. Value not set.",
|
f"{self._var_name}: Failed conversion of {value} to '{self._var_type.__name__}'. Value not set.",
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
setattr(state, self.name, value)
|
setattr(state, self._var_name, value)
|
||||||
|
|
||||||
setter.__qualname__ = self.get_setter_name()
|
setter.__qualname__ = self.get_setter_name()
|
||||||
|
|
||||||
return setter
|
return setter
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass(init=False, eq=False)
|
||||||
class ComputedVar(Var, property):
|
class ComputedVar(Var, property):
|
||||||
"""A field with computed getters."""
|
"""A field with computed getters."""
|
||||||
|
|
||||||
# Whether to track dependencies and cache computed values
|
# Whether to track dependencies and cache computed values
|
||||||
cache: bool = False
|
_cache: bool = dataclasses.field(default=False)
|
||||||
|
|
||||||
@property
|
def __init__(
|
||||||
def name(self) -> str:
|
self,
|
||||||
"""Get the name of the var.
|
fget: Callable[[State], Any],
|
||||||
|
fset: Callable[[State, Any], None] | None = None,
|
||||||
|
fdel: Callable[[State], Any] | None = None,
|
||||||
|
doc: str | None = None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
"""Initialize a ComputedVar.
|
||||||
|
|
||||||
Returns:
|
Args:
|
||||||
The name of the var.
|
fget: The getter function.
|
||||||
|
fset: The setter function.
|
||||||
|
fdel: The deleter function.
|
||||||
|
doc: The docstring.
|
||||||
|
**kwargs: additional attributes to set on the instance
|
||||||
"""
|
"""
|
||||||
assert self.fget is not None, "Var must have a getter."
|
property.__init__(self, fget, fset, fdel, doc)
|
||||||
return self.fget.__name__
|
kwargs["_var_name"] = kwargs.pop("_var_name", fget.__name__)
|
||||||
|
kwargs["_var_type"] = kwargs.pop("_var_type", self._determine_var_type())
|
||||||
|
BaseVar.__init__(self, **kwargs) # type: ignore
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def cache_attr(self) -> str:
|
def _cache_attr(self) -> str:
|
||||||
"""Get the attribute used to cache the value on the instance.
|
"""Get the attribute used to cache the value on the instance.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
An attribute name.
|
An attribute name.
|
||||||
"""
|
"""
|
||||||
return f"__cached_{self.name}"
|
return f"__cached_{self._var_name}"
|
||||||
|
|
||||||
def __get__(self, instance, owner):
|
def __get__(self, instance, owner):
|
||||||
"""Get the ComputedVar value.
|
"""Get the ComputedVar value.
|
||||||
@ -1282,15 +1354,15 @@ class ComputedVar(Var, property):
|
|||||||
Returns:
|
Returns:
|
||||||
The value of the var for the given instance.
|
The value of the var for the given instance.
|
||||||
"""
|
"""
|
||||||
if instance is None or not self.cache:
|
if instance is None or not self._cache:
|
||||||
return super().__get__(instance, owner)
|
return super().__get__(instance, owner)
|
||||||
|
|
||||||
# handle caching
|
# handle caching
|
||||||
if not hasattr(instance, self.cache_attr):
|
if not hasattr(instance, self._cache_attr):
|
||||||
setattr(instance, self.cache_attr, super().__get__(instance, owner))
|
setattr(instance, self._cache_attr, super().__get__(instance, owner))
|
||||||
return getattr(instance, self.cache_attr)
|
return getattr(instance, self._cache_attr)
|
||||||
|
|
||||||
def deps(
|
def _deps(
|
||||||
self,
|
self,
|
||||||
objclass: Type,
|
objclass: Type,
|
||||||
obj: FunctionType | CodeType | None = None,
|
obj: FunctionType | CodeType | None = None,
|
||||||
@ -1348,7 +1420,7 @@ class ComputedVar(Var, property):
|
|||||||
elif self_is_top_of_stack and instruction.opname == "LOAD_METHOD":
|
elif self_is_top_of_stack and instruction.opname == "LOAD_METHOD":
|
||||||
# method call on self
|
# method call on self
|
||||||
d.update(
|
d.update(
|
||||||
self.deps(
|
self._deps(
|
||||||
objclass=objclass,
|
objclass=objclass,
|
||||||
obj=getattr(objclass, instruction.argval),
|
obj=getattr(objclass, instruction.argval),
|
||||||
)
|
)
|
||||||
@ -1359,7 +1431,7 @@ class ComputedVar(Var, property):
|
|||||||
# recurse into nested functions / comprehensions, which can reference
|
# recurse into nested functions / comprehensions, which can reference
|
||||||
# instance attributes from the outer scope
|
# instance attributes from the outer scope
|
||||||
d.update(
|
d.update(
|
||||||
self.deps(
|
self._deps(
|
||||||
objclass=objclass,
|
objclass=objclass,
|
||||||
obj=instruction.argval,
|
obj=instruction.argval,
|
||||||
self_name=self_name,
|
self_name=self_name,
|
||||||
@ -1375,10 +1447,9 @@ class ComputedVar(Var, property):
|
|||||||
instance: the state instance that needs to recompute the value.
|
instance: the state instance that needs to recompute the value.
|
||||||
"""
|
"""
|
||||||
with contextlib.suppress(AttributeError):
|
with contextlib.suppress(AttributeError):
|
||||||
delattr(instance, self.cache_attr)
|
delattr(instance, self._cache_attr)
|
||||||
|
|
||||||
@property
|
def _determine_var_type(self) -> Type:
|
||||||
def type_(self):
|
|
||||||
"""Get the type of the var.
|
"""Get the type of the var.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@ -1403,7 +1474,7 @@ def cached_var(fget: Callable[[Any], Any]) -> ComputedVar:
|
|||||||
ComputedVar that is recomputed when dependencies change.
|
ComputedVar that is recomputed when dependencies change.
|
||||||
"""
|
"""
|
||||||
cvar = ComputedVar(fget=fget)
|
cvar = ComputedVar(fget=fget)
|
||||||
cvar.cache = True
|
cvar._cache = True
|
||||||
return cvar
|
return cvar
|
||||||
|
|
||||||
|
|
||||||
@ -1468,11 +1539,13 @@ def get_local_storage(key: Var | str | None = None) -> BaseVar:
|
|||||||
removal_version="0.3.0",
|
removal_version="0.3.0",
|
||||||
)
|
)
|
||||||
if key is not None:
|
if key is not None:
|
||||||
if not (isinstance(key, Var) and key.type_ == str) and not isinstance(key, str):
|
if not (isinstance(key, Var) and key._var_type == str) and not isinstance(
|
||||||
type_ = type(key) if not isinstance(key, Var) else key.type_
|
key, str
|
||||||
|
):
|
||||||
|
type_ = type(key) if not isinstance(key, Var) else key._var_type
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
f"Local storage keys can only be of type `str` or `var` of type `str`. Got `{type_}` instead."
|
f"Local storage keys can only be of type `str` or `var` of type `str`. Got `{type_}` instead."
|
||||||
)
|
)
|
||||||
key = key.full_name if isinstance(key, Var) else format.wrap(key, "'")
|
key = key._var_full_name if isinstance(key, Var) else format.wrap(key, "'")
|
||||||
return BaseVar(name=f"localStorage.getItem({key})", type_=str)
|
return BaseVar(_var_name=f"localStorage.getItem({key})", _var_type=str)
|
||||||
return BaseVar(name="getAllLocalStorageItems()", type_=Dict)
|
return BaseVar(_var_name="getAllLocalStorageItems()", _var_type=Dict)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
""" Generated with stubgen from mypy, then manually edited, do not regen."""
|
""" Generated with stubgen from mypy, then manually edited, do not regen."""
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
from _typeshed import Incomplete
|
from _typeshed import Incomplete
|
||||||
from abc import ABC
|
|
||||||
from reflex import constants as constants
|
from reflex import constants as constants
|
||||||
from reflex.base import Base as Base
|
from reflex.base import Base as Base
|
||||||
from reflex.state import State as State
|
from reflex.state import State as State
|
||||||
@ -23,19 +23,19 @@ USED_VARIABLES: Incomplete
|
|||||||
|
|
||||||
def get_unique_variable_name() -> str: ...
|
def get_unique_variable_name() -> str: ...
|
||||||
|
|
||||||
class Var(ABC):
|
class Var:
|
||||||
name: str
|
_var_name: str
|
||||||
type_: Type
|
_var_type: Type
|
||||||
state: str = ""
|
_var_state: str = ""
|
||||||
is_local: bool = False
|
_var_is_local: bool = False
|
||||||
is_string: bool = False
|
_var_is_string: bool = False
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(
|
def create(
|
||||||
cls, value: Any, is_local: bool = False, is_string: bool = False
|
cls, value: Any, _var_is_local: bool = False, _var_is_string: bool = False
|
||||||
) -> Optional[Var]: ...
|
) -> Optional[Var]: ...
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_safe(
|
def create_safe(
|
||||||
cls, value: Any, is_local: bool = False, is_string: bool = False
|
cls, value: Any, _var_is_local: bool = False, _var_is_string: bool = False
|
||||||
) -> Var: ...
|
) -> Var: ...
|
||||||
@classmethod
|
@classmethod
|
||||||
def __class_getitem__(cls, type_: str) -> _GenericAlias: ...
|
def __class_getitem__(cls, type_: str) -> _GenericAlias: ...
|
||||||
@ -87,31 +87,31 @@ class Var(ABC):
|
|||||||
def foreach(self, fn: Callable) -> Var: ...
|
def foreach(self, fn: Callable) -> Var: ...
|
||||||
def to(self, type_: Type) -> Var: ...
|
def to(self, type_: Type) -> Var: ...
|
||||||
@property
|
@property
|
||||||
def full_name(self) -> str: ...
|
def _var_full_name(self) -> str: ...
|
||||||
def set_state(self, state: Type[State]) -> Any: ...
|
def _var_set_state(self, state: Type[State]) -> Any: ...
|
||||||
|
|
||||||
class BaseVar(Var, Base):
|
@dataclass(eq=False)
|
||||||
name: str
|
class BaseVar(Var):
|
||||||
type_: Any
|
_var_name: str
|
||||||
state: str = ""
|
_var_type: Any
|
||||||
is_local: bool = False
|
_var_state: str = ""
|
||||||
is_string: bool = False
|
_var_is_local: bool = False
|
||||||
|
_var_is_string: bool = False
|
||||||
def __hash__(self) -> int: ...
|
def __hash__(self) -> int: ...
|
||||||
def get_default_value(self) -> Any: ...
|
def get_default_value(self) -> Any: ...
|
||||||
def get_setter_name(self, include_state: bool = ...) -> str: ...
|
def get_setter_name(self, include_state: bool = ...) -> str: ...
|
||||||
def get_setter(self) -> Callable[[State, Any], None]: ...
|
def get_setter(self) -> Callable[[State, Any], None]: ...
|
||||||
|
|
||||||
|
@dataclass(init=False)
|
||||||
class ComputedVar(Var):
|
class ComputedVar(Var):
|
||||||
cache: bool
|
_var_cache: bool
|
||||||
|
fget: FunctionType
|
||||||
@property
|
@property
|
||||||
def name(self) -> str: ...
|
def _cache_attr(self) -> str: ...
|
||||||
@property
|
|
||||||
def cache_attr(self) -> str: ...
|
|
||||||
def __get__(self, instance, owner): ...
|
def __get__(self, instance, owner): ...
|
||||||
def deps(self, objclass: Type, obj: Optional[FunctionType] = ...) -> Set[str]: ...
|
def _deps(self, objclass: Type, obj: Optional[FunctionType] = ...) -> Set[str]: ...
|
||||||
def mark_dirty(self, instance) -> None: ...
|
def mark_dirty(self, instance) -> None: ...
|
||||||
@property
|
def _determine_var_type(self) -> Type: ...
|
||||||
def type_(self): ...
|
|
||||||
def __init__(self, func) -> None: ...
|
def __init__(self, func) -> None: ...
|
||||||
|
|
||||||
def cached_var(fget: Callable[[Any], Any]) -> ComputedVar: ...
|
def cached_var(fget: Callable[[Any], Any]) -> ComputedVar: ...
|
||||||
|
@ -34,7 +34,7 @@ def test_validate_data_table(data_table_state: rx.Var, expected):
|
|||||||
expected: expected var name.
|
expected: expected var name.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if not types.is_dataframe(data_table_state.data.type_):
|
if not types.is_dataframe(data_table_state.data._var_type):
|
||||||
data_table_component = DataTable.create(
|
data_table_component = DataTable.create(
|
||||||
data=data_table_state.data, columns=data_table_state.columns
|
data=data_table_state.data, columns=data_table_state.columns
|
||||||
)
|
)
|
||||||
|
@ -51,7 +51,9 @@ def test_render_child_props():
|
|||||||
)._render()
|
)._render()
|
||||||
assert tag.props["sx"] == {"foo": "bar", "baz": "quuc"}
|
assert tag.props["sx"] == {"foo": "bar", "baz": "quuc"}
|
||||||
assert tag.props["value"].equals(
|
assert tag.props["value"].equals(
|
||||||
BaseVar(name="real", type_=str, is_local=True, is_string=False)
|
BaseVar(
|
||||||
|
_var_name="real", _var_type=str, _var_is_local=True, _var_is_string=False
|
||||||
|
)
|
||||||
)
|
)
|
||||||
assert len(tag.props["onChange"].events) == 1
|
assert len(tag.props["onChange"].events) == 1
|
||||||
assert tag.props["onChange"].events[0].handler == S.on_change
|
assert tag.props["onChange"].events[0].handler == S.on_change
|
||||||
@ -86,11 +88,13 @@ def test_render_child_props_recursive():
|
|||||||
)._render()
|
)._render()
|
||||||
assert tag.props["sx"] == {"foo": "bar", "baz": "quuc"}
|
assert tag.props["sx"] == {"foo": "bar", "baz": "quuc"}
|
||||||
assert tag.props["value"].equals(
|
assert tag.props["value"].equals(
|
||||||
BaseVar(name="outer", type_=str, is_local=True, is_string=False)
|
BaseVar(
|
||||||
|
_var_name="outer", _var_type=str, _var_is_local=True, _var_is_string=False
|
||||||
|
)
|
||||||
)
|
)
|
||||||
assert tag.props["forceNotifyOnBlur"].name == "false"
|
assert tag.props["forceNotifyOnBlur"]._var_name == "false"
|
||||||
assert tag.props["forceNotifyByEnter"].name == "false"
|
assert tag.props["forceNotifyByEnter"]._var_name == "false"
|
||||||
assert tag.props["debounceTimeout"].name == "42"
|
assert tag.props["debounceTimeout"]._var_name == "42"
|
||||||
assert len(tag.props["onChange"].events) == 1
|
assert len(tag.props["onChange"].events) == 1
|
||||||
assert tag.props["onChange"].events[0].handler == S.on_change
|
assert tag.props["onChange"].events[0].handler == S.on_change
|
||||||
assert tag.contents == ""
|
assert tag.contents == ""
|
||||||
@ -102,7 +106,7 @@ def test_full_control_implicit_debounce():
|
|||||||
value=S.value,
|
value=S.value,
|
||||||
on_change=S.on_change,
|
on_change=S.on_change,
|
||||||
)._render()
|
)._render()
|
||||||
assert tag.props["debounceTimeout"].name == "50"
|
assert tag.props["debounceTimeout"]._var_name == "50"
|
||||||
assert len(tag.props["onChange"].events) == 1
|
assert len(tag.props["onChange"].events) == 1
|
||||||
assert tag.props["onChange"].events[0].handler == S.on_change
|
assert tag.props["onChange"].events[0].handler == S.on_change
|
||||||
assert tag.contents == ""
|
assert tag.contents == ""
|
||||||
@ -114,7 +118,7 @@ def test_full_control_implicit_debounce_text_area():
|
|||||||
value=S.value,
|
value=S.value,
|
||||||
on_change=S.on_change,
|
on_change=S.on_change,
|
||||||
)._render()
|
)._render()
|
||||||
assert tag.props["debounceTimeout"].name == "50"
|
assert tag.props["debounceTimeout"]._var_name == "50"
|
||||||
assert len(tag.props["onChange"].events) == 1
|
assert len(tag.props["onChange"].events) == 1
|
||||||
assert tag.props["onChange"].events[0].handler == S.on_change
|
assert tag.props["onChange"].events[0].handler == S.on_change
|
||||||
assert tag.contents == ""
|
assert tag.contents == ""
|
||||||
|
@ -105,7 +105,7 @@ def test_cond_no_else():
|
|||||||
comp = comp.children[0]
|
comp = comp.children[0]
|
||||||
assert isinstance(comp, Cond)
|
assert isinstance(comp, Cond)
|
||||||
assert comp.cond._decode() is True # type: ignore
|
assert comp.cond._decode() is True # type: ignore
|
||||||
assert comp.comp1 == Fragment.create(Text.create("hello"))
|
assert comp.comp1.render() == Fragment.create(Text.create("hello")).render()
|
||||||
assert comp.comp2 == Fragment.create()
|
assert comp.comp2 == Fragment.create()
|
||||||
|
|
||||||
# Props do not support the use of cond without else
|
# Props do not support the use of cond without else
|
||||||
|
@ -186,6 +186,6 @@ def test_foreach_render(state_var, render_fn, render_dict):
|
|||||||
rend = component.render()
|
rend = component.render()
|
||||||
arg_index = rend["arg_index"]
|
arg_index = rend["arg_index"]
|
||||||
assert rend["iterable_state"] == render_dict["iterable_state"]
|
assert rend["iterable_state"] == render_dict["iterable_state"]
|
||||||
assert arg_index.name == render_dict["arg_index"]
|
assert arg_index._var_name == render_dict["arg_index"]
|
||||||
assert arg_index.type_ == int
|
assert arg_index._var_type == int
|
||||||
assert rend["iterable_type"] == render_dict["iterable_type"]
|
assert rend["iterable_type"] == render_dict["iterable_type"]
|
||||||
|
@ -43,7 +43,7 @@ try:
|
|||||||
pil_image: The image to serialize.
|
pil_image: The image to serialize.
|
||||||
"""
|
"""
|
||||||
image = Image.create(src=pil_image)
|
image = Image.create(src=pil_image)
|
||||||
assert str(image.src.name) == serialize_image(pil_image) # type: ignore
|
assert str(image.src._var_name) == serialize_image(pil_image) # type: ignore
|
||||||
|
|
||||||
def test_render(pil_image: Img):
|
def test_render(pil_image: Img):
|
||||||
"""Test that rendering an image works.
|
"""Test that rendering an image works.
|
||||||
@ -52,7 +52,7 @@ try:
|
|||||||
pil_image: The image to serialize.
|
pil_image: The image to serialize.
|
||||||
"""
|
"""
|
||||||
image = Image.create(src=pil_image)
|
image = Image.create(src=pil_image)
|
||||||
assert image.src.is_string # type: ignore
|
assert image.src._var_is_string # type: ignore
|
||||||
|
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@ def test_format_cond_tag():
|
|||||||
tag = CondTag(
|
tag = CondTag(
|
||||||
true_value=dict(Tag(name="h1", contents="True content")),
|
true_value=dict(Tag(name="h1", contents="True content")),
|
||||||
false_value=dict(Tag(name="h2", contents="False content")),
|
false_value=dict(Tag(name="h2", contents="False content")),
|
||||||
cond=BaseVar(name="logged_in", type_=bool),
|
cond=BaseVar(_var_name="logged_in", _var_type=bool),
|
||||||
)
|
)
|
||||||
tag_dict = dict(tag)
|
tag_dict = dict(tag)
|
||||||
cond, true_value, false_value = (
|
cond, true_value, false_value = (
|
||||||
@ -117,8 +117,8 @@ def test_format_cond_tag():
|
|||||||
tag_dict["true_value"],
|
tag_dict["true_value"],
|
||||||
tag_dict["false_value"],
|
tag_dict["false_value"],
|
||||||
)
|
)
|
||||||
assert cond.name == "logged_in"
|
assert cond._var_name == "logged_in"
|
||||||
assert cond.type_ == bool
|
assert cond._var_type == bool
|
||||||
|
|
||||||
assert true_value["name"] == "h1"
|
assert true_value["name"] == "h1"
|
||||||
assert true_value["contents"] == "True content"
|
assert true_value["contents"] == "True content"
|
||||||
|
@ -250,7 +250,7 @@ def test_add_page_set_route_dynamic(app: App, index_page, windows_platform: bool
|
|||||||
app.add_page(index_page, route=route)
|
app.add_page(index_page, route=route)
|
||||||
assert set(app.pages.keys()) == {"test/[dynamic]"}
|
assert set(app.pages.keys()) == {"test/[dynamic]"}
|
||||||
assert "dynamic" in app.state.computed_vars
|
assert "dynamic" in app.state.computed_vars
|
||||||
assert app.state.computed_vars["dynamic"].deps(objclass=DefaultState) == {
|
assert app.state.computed_vars["dynamic"]._deps(objclass=DefaultState) == {
|
||||||
constants.ROUTER_DATA
|
constants.ROUTER_DATA
|
||||||
}
|
}
|
||||||
assert constants.ROUTER_DATA in app.state().computed_var_dependencies
|
assert constants.ROUTER_DATA in app.state().computed_var_dependencies
|
||||||
@ -869,7 +869,7 @@ async def test_dynamic_route_var_route_change_completed_on_load(
|
|||||||
app.add_page(index_page, route=route, on_load=DynamicState.on_load) # type: ignore
|
app.add_page(index_page, route=route, on_load=DynamicState.on_load) # type: ignore
|
||||||
assert arg_name in app.state.vars
|
assert arg_name in app.state.vars
|
||||||
assert arg_name in app.state.computed_vars
|
assert arg_name in app.state.computed_vars
|
||||||
assert app.state.computed_vars[arg_name].deps(objclass=DynamicState) == {
|
assert app.state.computed_vars[arg_name]._deps(objclass=DynamicState) == {
|
||||||
constants.ROUTER_DATA
|
constants.ROUTER_DATA
|
||||||
}
|
}
|
||||||
assert constants.ROUTER_DATA in app.state().computed_var_dependencies
|
assert constants.ROUTER_DATA in app.state().computed_var_dependencies
|
||||||
|
@ -197,11 +197,11 @@ def test_base_class_vars(test_state):
|
|||||||
continue
|
continue
|
||||||
prop = getattr(cls, field)
|
prop = getattr(cls, field)
|
||||||
assert isinstance(prop, BaseVar)
|
assert isinstance(prop, BaseVar)
|
||||||
assert prop.name == field
|
assert prop._var_name == field
|
||||||
|
|
||||||
assert cls.num1.type_ == int
|
assert cls.num1._var_type == int
|
||||||
assert cls.num2.type_ == float
|
assert cls.num2._var_type == float
|
||||||
assert cls.key.type_ == str
|
assert cls.key._var_type == str
|
||||||
|
|
||||||
|
|
||||||
def test_computed_class_var(test_state):
|
def test_computed_class_var(test_state):
|
||||||
@ -211,7 +211,7 @@ def test_computed_class_var(test_state):
|
|||||||
test_state: A state.
|
test_state: A state.
|
||||||
"""
|
"""
|
||||||
cls = type(test_state)
|
cls = type(test_state)
|
||||||
vars = [(prop.name, prop.type_) for prop in cls.computed_vars.values()]
|
vars = [(prop._var_name, prop._var_type) for prop in cls.computed_vars.values()]
|
||||||
assert ("sum", float) in vars
|
assert ("sum", float) in vars
|
||||||
assert ("upper", str) in vars
|
assert ("upper", str) in vars
|
||||||
|
|
||||||
@ -416,11 +416,13 @@ def test_set_class_var():
|
|||||||
"""Test setting the var of a class."""
|
"""Test setting the var of a class."""
|
||||||
with pytest.raises(AttributeError):
|
with pytest.raises(AttributeError):
|
||||||
TestState.num3 # type: ignore
|
TestState.num3 # type: ignore
|
||||||
TestState._set_var(BaseVar(name="num3", type_=int).set_state(TestState))
|
TestState._set_var(
|
||||||
|
BaseVar(_var_name="num3", _var_type=int)._var_set_state(TestState)
|
||||||
|
)
|
||||||
var = TestState.num3 # type: ignore
|
var = TestState.num3 # type: ignore
|
||||||
assert var.name == "num3"
|
assert var._var_name == "num3"
|
||||||
assert var.type_ == int
|
assert var._var_type == int
|
||||||
assert var.state == TestState.get_full_name()
|
assert var._var_state == TestState.get_full_name()
|
||||||
|
|
||||||
|
|
||||||
def test_set_parent_and_substates(test_state, child_state, grandchild_state):
|
def test_set_parent_and_substates(test_state, child_state, grandchild_state):
|
||||||
@ -984,7 +986,7 @@ def test_conditional_computed_vars():
|
|||||||
assert ms._dirty_computed_vars(from_vars={"flag"}) == {"rendered_var"}
|
assert ms._dirty_computed_vars(from_vars={"flag"}) == {"rendered_var"}
|
||||||
assert ms._dirty_computed_vars(from_vars={"t2"}) == {"rendered_var"}
|
assert ms._dirty_computed_vars(from_vars={"t2"}) == {"rendered_var"}
|
||||||
assert ms._dirty_computed_vars(from_vars={"t1"}) == {"rendered_var"}
|
assert ms._dirty_computed_vars(from_vars={"t1"}) == {"rendered_var"}
|
||||||
assert ms.computed_vars["rendered_var"].deps(objclass=MainState) == {
|
assert ms.computed_vars["rendered_var"]._deps(objclass=MainState) == {
|
||||||
"flag",
|
"flag",
|
||||||
"t1",
|
"t1",
|
||||||
"t2",
|
"t2",
|
||||||
|
@ -16,11 +16,11 @@ from reflex.vars import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
test_vars = [
|
test_vars = [
|
||||||
BaseVar(name="prop1", type_=int),
|
BaseVar(_var_name="prop1", _var_type=int),
|
||||||
BaseVar(name="key", type_=str),
|
BaseVar(_var_name="key", _var_type=str),
|
||||||
BaseVar(name="value", type_=str, state="state"),
|
BaseVar(_var_name="value", _var_type=str, _var_state="state"),
|
||||||
BaseVar(name="local", type_=str, state="state", is_local=True),
|
BaseVar(_var_name="local", _var_type=str, _var_state="state", _var_is_local=True),
|
||||||
BaseVar(name="local2", type_=str, is_local=True),
|
BaseVar(_var_name="local2", _var_type=str, _var_is_local=True),
|
||||||
]
|
]
|
||||||
|
|
||||||
test_import_vars = [ImportVar(tag="DataGrid"), ImportVar(tag="DataGrid", alias="Grid")]
|
test_import_vars = [ImportVar(tag="DataGrid"), ImportVar(tag="DataGrid", alias="Grid")]
|
||||||
@ -124,7 +124,7 @@ def test_full_name(prop, expected):
|
|||||||
prop: The var to test.
|
prop: The var to test.
|
||||||
expected: The expected full name.
|
expected: The expected full name.
|
||||||
"""
|
"""
|
||||||
assert prop.full_name == expected
|
assert prop._var_full_name == expected
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
@ -147,14 +147,14 @@ def test_str(prop, expected):
|
|||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"prop,expected",
|
"prop,expected",
|
||||||
[
|
[
|
||||||
(BaseVar(name="p", type_=int), 0),
|
(BaseVar(_var_name="p", _var_type=int), 0),
|
||||||
(BaseVar(name="p", type_=float), 0.0),
|
(BaseVar(_var_name="p", _var_type=float), 0.0),
|
||||||
(BaseVar(name="p", type_=str), ""),
|
(BaseVar(_var_name="p", _var_type=str), ""),
|
||||||
(BaseVar(name="p", type_=bool), False),
|
(BaseVar(_var_name="p", _var_type=bool), False),
|
||||||
(BaseVar(name="p", type_=list), []),
|
(BaseVar(_var_name="p", _var_type=list), []),
|
||||||
(BaseVar(name="p", type_=dict), {}),
|
(BaseVar(_var_name="p", _var_type=dict), {}),
|
||||||
(BaseVar(name="p", type_=tuple), ()),
|
(BaseVar(_var_name="p", _var_type=tuple), ()),
|
||||||
(BaseVar(name="p", type_=set), set()),
|
(BaseVar(_var_name="p", _var_type=set), set()),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_default_value(prop, expected):
|
def test_default_value(prop, expected):
|
||||||
@ -194,13 +194,13 @@ def test_get_setter(prop, expected):
|
|||||||
"value,expected",
|
"value,expected",
|
||||||
[
|
[
|
||||||
(None, None),
|
(None, None),
|
||||||
(1, BaseVar(name="1", type_=int, is_local=True)),
|
(1, BaseVar(_var_name="1", _var_type=int, _var_is_local=True)),
|
||||||
("key", BaseVar(name="key", type_=str, is_local=True)),
|
("key", BaseVar(_var_name="key", _var_type=str, _var_is_local=True)),
|
||||||
(3.14, BaseVar(name="3.14", type_=float, is_local=True)),
|
(3.14, BaseVar(_var_name="3.14", _var_type=float, _var_is_local=True)),
|
||||||
([1, 2, 3], BaseVar(name="[1, 2, 3]", type_=list, is_local=True)),
|
([1, 2, 3], BaseVar(_var_name="[1, 2, 3]", _var_type=list, _var_is_local=True)),
|
||||||
(
|
(
|
||||||
{"a": 1, "b": 2},
|
{"a": 1, "b": 2},
|
||||||
BaseVar(name='{"a": 1, "b": 2}', type_=dict, is_local=True),
|
BaseVar(_var_name='{"a": 1, "b": 2}', _var_type=dict, _var_is_local=True),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@ -232,9 +232,9 @@ def test_create_type_error():
|
|||||||
|
|
||||||
def v(value) -> Var:
|
def v(value) -> Var:
|
||||||
val = (
|
val = (
|
||||||
Var.create(json.dumps(value), is_string=True, is_local=False)
|
Var.create(json.dumps(value), _var_is_string=True, _var_is_local=False)
|
||||||
if isinstance(value, str)
|
if isinstance(value, str)
|
||||||
else Var.create(value, is_local=False)
|
else Var.create(value, _var_is_local=False)
|
||||||
)
|
)
|
||||||
assert val is not None
|
assert val is not None
|
||||||
return val
|
return val
|
||||||
@ -264,7 +264,8 @@ def test_basic_operations(TestObj):
|
|||||||
assert str(v([1, 2, 3])[v(0)]) == "{[1, 2, 3].at(0)}"
|
assert str(v([1, 2, 3])[v(0)]) == "{[1, 2, 3].at(0)}"
|
||||||
assert str(v({"a": 1, "b": 2})["a"]) == '{{"a": 1, "b": 2}["a"]}'
|
assert str(v({"a": 1, "b": 2})["a"]) == '{{"a": 1, "b": 2}["a"]}'
|
||||||
assert (
|
assert (
|
||||||
str(BaseVar(name="foo", state="state", type_=TestObj).bar) == "{state.foo.bar}"
|
str(BaseVar(_var_name="foo", _var_state="state", _var_type=TestObj).bar)
|
||||||
|
== "{state.foo.bar}"
|
||||||
)
|
)
|
||||||
assert str(abs(v(1))) == "{Math.abs(1)}"
|
assert str(abs(v(1))) == "{Math.abs(1)}"
|
||||||
assert str(v([1, 2, 3]).length()) == "{[1, 2, 3].length}"
|
assert str(v([1, 2, 3]).length()) == "{[1, 2, 3].length}"
|
||||||
@ -274,10 +275,13 @@ def test_basic_operations(TestObj):
|
|||||||
assert str(v([1, 2, 3]).reverse()) == "{[...[1, 2, 3]].reverse()}"
|
assert str(v([1, 2, 3]).reverse()) == "{[...[1, 2, 3]].reverse()}"
|
||||||
assert str(v(["1", "2", "3"]).reverse()) == '{[...["1", "2", "3"]].reverse()}'
|
assert str(v(["1", "2", "3"]).reverse()) == '{[...["1", "2", "3"]].reverse()}'
|
||||||
assert (
|
assert (
|
||||||
str(BaseVar(name="foo", state="state", type_=list).reverse())
|
str(BaseVar(_var_name="foo", _var_state="state", _var_type=list).reverse())
|
||||||
== "{[...state.foo].reverse()}"
|
== "{[...state.foo].reverse()}"
|
||||||
)
|
)
|
||||||
assert str(BaseVar(name="foo", type_=list).reverse()) == "{[...foo].reverse()}"
|
assert (
|
||||||
|
str(BaseVar(_var_name="foo", _var_type=list).reverse())
|
||||||
|
== "{[...foo].reverse()}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
@ -285,12 +289,12 @@ def test_basic_operations(TestObj):
|
|||||||
[
|
[
|
||||||
(v([1, 2, 3]), "[1, 2, 3]"),
|
(v([1, 2, 3]), "[1, 2, 3]"),
|
||||||
(v(["1", "2", "3"]), '["1", "2", "3"]'),
|
(v(["1", "2", "3"]), '["1", "2", "3"]'),
|
||||||
(BaseVar(name="foo", state="state", type_=list), "state.foo"),
|
(BaseVar(_var_name="foo", _var_state="state", _var_type=list), "state.foo"),
|
||||||
(BaseVar(name="foo", type_=list), "foo"),
|
(BaseVar(_var_name="foo", _var_type=list), "foo"),
|
||||||
(v((1, 2, 3)), "[1, 2, 3]"),
|
(v((1, 2, 3)), "[1, 2, 3]"),
|
||||||
(v(("1", "2", "3")), '["1", "2", "3"]'),
|
(v(("1", "2", "3")), '["1", "2", "3"]'),
|
||||||
(BaseVar(name="foo", state="state", type_=tuple), "state.foo"),
|
(BaseVar(_var_name="foo", _var_state="state", _var_type=tuple), "state.foo"),
|
||||||
(BaseVar(name="foo", type_=tuple), "foo"),
|
(BaseVar(_var_name="foo", _var_type=tuple), "foo"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_list_tuple_contains(var, expected):
|
def test_list_tuple_contains(var, expected):
|
||||||
@ -298,8 +302,8 @@ def test_list_tuple_contains(var, expected):
|
|||||||
assert str(var.contains("1")) == f'{{{expected}.includes("1")}}'
|
assert str(var.contains("1")) == f'{{{expected}.includes("1")}}'
|
||||||
assert str(var.contains(v(1))) == f"{{{expected}.includes(1)}}"
|
assert str(var.contains(v(1))) == f"{{{expected}.includes(1)}}"
|
||||||
assert str(var.contains(v("1"))) == f'{{{expected}.includes("1")}}'
|
assert str(var.contains(v("1"))) == f'{{{expected}.includes("1")}}'
|
||||||
other_state_var = BaseVar(name="other", state="state", type_=str)
|
other_state_var = BaseVar(_var_name="other", _var_state="state", _var_type=str)
|
||||||
other_var = BaseVar(name="other", type_=str)
|
other_var = BaseVar(_var_name="other", _var_type=str)
|
||||||
assert str(var.contains(other_state_var)) == f"{{{expected}.includes(state.other)}}"
|
assert str(var.contains(other_state_var)) == f"{{{expected}.includes(state.other)}}"
|
||||||
assert str(var.contains(other_var)) == f"{{{expected}.includes(other)}}"
|
assert str(var.contains(other_var)) == f"{{{expected}.includes(other)}}"
|
||||||
|
|
||||||
@ -308,15 +312,15 @@ def test_list_tuple_contains(var, expected):
|
|||||||
"var, expected",
|
"var, expected",
|
||||||
[
|
[
|
||||||
(v("123"), json.dumps("123")),
|
(v("123"), json.dumps("123")),
|
||||||
(BaseVar(name="foo", state="state", type_=str), "state.foo"),
|
(BaseVar(_var_name="foo", _var_state="state", _var_type=str), "state.foo"),
|
||||||
(BaseVar(name="foo", type_=str), "foo"),
|
(BaseVar(_var_name="foo", _var_type=str), "foo"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_str_contains(var, expected):
|
def test_str_contains(var, expected):
|
||||||
assert str(var.contains("1")) == f'{{{expected}.includes("1")}}'
|
assert str(var.contains("1")) == f'{{{expected}.includes("1")}}'
|
||||||
assert str(var.contains(v("1"))) == f'{{{expected}.includes("1")}}'
|
assert str(var.contains(v("1"))) == f'{{{expected}.includes("1")}}'
|
||||||
other_state_var = BaseVar(name="other", state="state", type_=str)
|
other_state_var = BaseVar(_var_name="other", _var_state="state", _var_type=str)
|
||||||
other_var = BaseVar(name="other", type_=str)
|
other_var = BaseVar(_var_name="other", _var_type=str)
|
||||||
assert str(var.contains(other_state_var)) == f"{{{expected}.includes(state.other)}}"
|
assert str(var.contains(other_state_var)) == f"{{{expected}.includes(state.other)}}"
|
||||||
assert str(var.contains(other_var)) == f"{{{expected}.includes(other)}}"
|
assert str(var.contains(other_var)) == f"{{{expected}.includes(other)}}"
|
||||||
|
|
||||||
@ -325,8 +329,8 @@ def test_str_contains(var, expected):
|
|||||||
"var, expected",
|
"var, expected",
|
||||||
[
|
[
|
||||||
(v({"a": 1, "b": 2}), '{"a": 1, "b": 2}'),
|
(v({"a": 1, "b": 2}), '{"a": 1, "b": 2}'),
|
||||||
(BaseVar(name="foo", state="state", type_=dict), "state.foo"),
|
(BaseVar(_var_name="foo", _var_state="state", _var_type=dict), "state.foo"),
|
||||||
(BaseVar(name="foo", type_=dict), "foo"),
|
(BaseVar(_var_name="foo", _var_type=dict), "foo"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_dict_contains(var, expected):
|
def test_dict_contains(var, expected):
|
||||||
@ -334,8 +338,8 @@ def test_dict_contains(var, expected):
|
|||||||
assert str(var.contains("1")) == f'{{{expected}.hasOwnProperty("1")}}'
|
assert str(var.contains("1")) == f'{{{expected}.hasOwnProperty("1")}}'
|
||||||
assert str(var.contains(v(1))) == f"{{{expected}.hasOwnProperty(1)}}"
|
assert str(var.contains(v(1))) == f"{{{expected}.hasOwnProperty(1)}}"
|
||||||
assert str(var.contains(v("1"))) == f'{{{expected}.hasOwnProperty("1")}}'
|
assert str(var.contains(v("1"))) == f'{{{expected}.hasOwnProperty("1")}}'
|
||||||
other_state_var = BaseVar(name="other", state="state", type_=str)
|
other_state_var = BaseVar(_var_name="other", _var_state="state", _var_type=str)
|
||||||
other_var = BaseVar(name="other", type_=str)
|
other_var = BaseVar(_var_name="other", _var_type=str)
|
||||||
assert (
|
assert (
|
||||||
str(var.contains(other_state_var))
|
str(var.contains(other_state_var))
|
||||||
== f"{{{expected}.hasOwnProperty(state.other)}}"
|
== f"{{{expected}.hasOwnProperty(state.other)}}"
|
||||||
@ -346,9 +350,9 @@ def test_dict_contains(var, expected):
|
|||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"var",
|
"var",
|
||||||
[
|
[
|
||||||
BaseVar(name="list", type_=List[int]),
|
BaseVar(_var_name="list", _var_type=List[int]),
|
||||||
BaseVar(name="tuple", type_=Tuple[int, int]),
|
BaseVar(_var_name="tuple", _var_type=Tuple[int, int]),
|
||||||
BaseVar(name="str", type_=str),
|
BaseVar(_var_name="str", _var_type=str),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_var_indexing_lists(var):
|
def test_var_indexing_lists(var):
|
||||||
@ -358,49 +362,70 @@ def test_var_indexing_lists(var):
|
|||||||
var : The str, list or tuple base var.
|
var : The str, list or tuple base var.
|
||||||
"""
|
"""
|
||||||
# Test basic indexing.
|
# Test basic indexing.
|
||||||
assert str(var[0]) == f"{{{var.name}.at(0)}}"
|
assert str(var[0]) == f"{{{var._var_name}.at(0)}}"
|
||||||
assert str(var[1]) == f"{{{var.name}.at(1)}}"
|
assert str(var[1]) == f"{{{var._var_name}.at(1)}}"
|
||||||
|
|
||||||
# Test negative indexing.
|
# Test negative indexing.
|
||||||
assert str(var[-1]) == f"{{{var.name}.at(-1)}}"
|
assert str(var[-1]) == f"{{{var._var_name}.at(-1)}}"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"var, index",
|
"var, index",
|
||||||
[
|
[
|
||||||
(BaseVar(name="lst", type_=List[int]), [1, 2]),
|
(BaseVar(_var_name="lst", _var_type=List[int]), [1, 2]),
|
||||||
(BaseVar(name="lst", type_=List[int]), {"name": "dict"}),
|
(BaseVar(_var_name="lst", _var_type=List[int]), {"name": "dict"}),
|
||||||
(BaseVar(name="lst", type_=List[int]), {"set"}),
|
(BaseVar(_var_name="lst", _var_type=List[int]), {"set"}),
|
||||||
(
|
(
|
||||||
BaseVar(name="lst", type_=List[int]),
|
BaseVar(_var_name="lst", _var_type=List[int]),
|
||||||
(
|
(
|
||||||
1,
|
1,
|
||||||
2,
|
2,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
(BaseVar(name="lst", type_=List[int]), 1.5),
|
(BaseVar(_var_name="lst", _var_type=List[int]), 1.5),
|
||||||
(BaseVar(name="lst", type_=List[int]), "str"),
|
(BaseVar(_var_name="lst", _var_type=List[int]), "str"),
|
||||||
(BaseVar(name="lst", type_=List[int]), BaseVar(name="string_var", type_=str)),
|
|
||||||
(BaseVar(name="lst", type_=List[int]), BaseVar(name="float_var", type_=float)),
|
|
||||||
(
|
(
|
||||||
BaseVar(name="lst", type_=List[int]),
|
BaseVar(_var_name="lst", _var_type=List[int]),
|
||||||
BaseVar(name="list_var", type_=List[int]),
|
BaseVar(_var_name="string_var", _var_type=str),
|
||||||
),
|
),
|
||||||
(BaseVar(name="lst", type_=List[int]), BaseVar(name="set_var", type_=Set[str])),
|
|
||||||
(
|
(
|
||||||
BaseVar(name="lst", type_=List[int]),
|
BaseVar(_var_name="lst", _var_type=List[int]),
|
||||||
BaseVar(name="dict_var", type_=Dict[str, str]),
|
BaseVar(_var_name="float_var", _var_type=float),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
BaseVar(_var_name="lst", _var_type=List[int]),
|
||||||
|
BaseVar(_var_name="list_var", _var_type=List[int]),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
BaseVar(_var_name="lst", _var_type=List[int]),
|
||||||
|
BaseVar(_var_name="set_var", _var_type=Set[str]),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
BaseVar(_var_name="lst", _var_type=List[int]),
|
||||||
|
BaseVar(_var_name="dict_var", _var_type=Dict[str, str]),
|
||||||
|
),
|
||||||
|
(BaseVar(_var_name="str", _var_type=str), [1, 2]),
|
||||||
|
(BaseVar(_var_name="lst", _var_type=str), {"name": "dict"}),
|
||||||
|
(BaseVar(_var_name="lst", _var_type=str), {"set"}),
|
||||||
|
(
|
||||||
|
BaseVar(_var_name="lst", _var_type=str),
|
||||||
|
BaseVar(_var_name="string_var", _var_type=str),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
BaseVar(_var_name="lst", _var_type=str),
|
||||||
|
BaseVar(_var_name="float_var", _var_type=float),
|
||||||
|
),
|
||||||
|
(BaseVar(_var_name="str", _var_type=Tuple[str]), [1, 2]),
|
||||||
|
(BaseVar(_var_name="lst", _var_type=Tuple[str]), {"name": "dict"}),
|
||||||
|
(BaseVar(_var_name="lst", _var_type=Tuple[str]), {"set"}),
|
||||||
|
(
|
||||||
|
BaseVar(_var_name="lst", _var_type=Tuple[str]),
|
||||||
|
BaseVar(_var_name="string_var", _var_type=str),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
BaseVar(_var_name="lst", _var_type=Tuple[str]),
|
||||||
|
BaseVar(_var_name="float_var", _var_type=float),
|
||||||
),
|
),
|
||||||
(BaseVar(name="str", type_=str), [1, 2]),
|
|
||||||
(BaseVar(name="lst", type_=str), {"name": "dict"}),
|
|
||||||
(BaseVar(name="lst", type_=str), {"set"}),
|
|
||||||
(BaseVar(name="lst", type_=str), BaseVar(name="string_var", type_=str)),
|
|
||||||
(BaseVar(name="lst", type_=str), BaseVar(name="float_var", type_=float)),
|
|
||||||
(BaseVar(name="str", type_=Tuple[str]), [1, 2]),
|
|
||||||
(BaseVar(name="lst", type_=Tuple[str]), {"name": "dict"}),
|
|
||||||
(BaseVar(name="lst", type_=Tuple[str]), {"set"}),
|
|
||||||
(BaseVar(name="lst", type_=Tuple[str]), BaseVar(name="string_var", type_=str)),
|
|
||||||
(BaseVar(name="lst", type_=Tuple[str]), BaseVar(name="float_var", type_=float)),
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_var_unsupported_indexing_lists(var, index):
|
def test_var_unsupported_indexing_lists(var, index):
|
||||||
@ -417,9 +442,9 @@ def test_var_unsupported_indexing_lists(var, index):
|
|||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"var",
|
"var",
|
||||||
[
|
[
|
||||||
BaseVar(name="lst", type_=List[int]),
|
BaseVar(_var_name="lst", _var_type=List[int]),
|
||||||
BaseVar(name="tuple", type_=Tuple[int, int]),
|
BaseVar(_var_name="tuple", _var_type=Tuple[int, int]),
|
||||||
BaseVar(name="str", type_=str),
|
BaseVar(_var_name="str", _var_type=str),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_var_list_slicing(var):
|
def test_var_list_slicing(var):
|
||||||
@ -428,14 +453,14 @@ def test_var_list_slicing(var):
|
|||||||
Args:
|
Args:
|
||||||
var : The str, list or tuple base var.
|
var : The str, list or tuple base var.
|
||||||
"""
|
"""
|
||||||
assert str(var[:1]) == f"{{{var.name}.slice(0, 1)}}"
|
assert str(var[:1]) == f"{{{var._var_name}.slice(0, 1)}}"
|
||||||
assert str(var[:1]) == f"{{{var.name}.slice(0, 1)}}"
|
assert str(var[:1]) == f"{{{var._var_name}.slice(0, 1)}}"
|
||||||
assert str(var[:]) == f"{{{var.name}.slice(0, undefined)}}"
|
assert str(var[:]) == f"{{{var._var_name}.slice(0, undefined)}}"
|
||||||
|
|
||||||
|
|
||||||
def test_dict_indexing():
|
def test_dict_indexing():
|
||||||
"""Test that we can index into dict vars."""
|
"""Test that we can index into dict vars."""
|
||||||
dct = BaseVar(name="dct", type_=Dict[str, int])
|
dct = BaseVar(_var_name="dct", _var_type=Dict[str, int])
|
||||||
|
|
||||||
# Check correct indexing.
|
# Check correct indexing.
|
||||||
assert str(dct["a"]) == '{dct["a"]}'
|
assert str(dct["a"]) == '{dct["a"]}'
|
||||||
@ -446,66 +471,66 @@ def test_dict_indexing():
|
|||||||
"var, index",
|
"var, index",
|
||||||
[
|
[
|
||||||
(
|
(
|
||||||
BaseVar(name="dict", type_=Dict[str, str]),
|
BaseVar(_var_name="dict", _var_type=Dict[str, str]),
|
||||||
[1, 2],
|
[1, 2],
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
BaseVar(name="dict", type_=Dict[str, str]),
|
BaseVar(_var_name="dict", _var_type=Dict[str, str]),
|
||||||
{"name": "dict"},
|
{"name": "dict"},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
BaseVar(name="dict", type_=Dict[str, str]),
|
BaseVar(_var_name="dict", _var_type=Dict[str, str]),
|
||||||
{"set"},
|
{"set"},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
BaseVar(name="dict", type_=Dict[str, str]),
|
BaseVar(_var_name="dict", _var_type=Dict[str, str]),
|
||||||
(
|
(
|
||||||
1,
|
1,
|
||||||
2,
|
2,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
BaseVar(name="lst", type_=Dict[str, str]),
|
BaseVar(_var_name="lst", _var_type=Dict[str, str]),
|
||||||
BaseVar(name="list_var", type_=List[int]),
|
BaseVar(_var_name="list_var", _var_type=List[int]),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
BaseVar(name="lst", type_=Dict[str, str]),
|
BaseVar(_var_name="lst", _var_type=Dict[str, str]),
|
||||||
BaseVar(name="set_var", type_=Set[str]),
|
BaseVar(_var_name="set_var", _var_type=Set[str]),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
BaseVar(name="lst", type_=Dict[str, str]),
|
BaseVar(_var_name="lst", _var_type=Dict[str, str]),
|
||||||
BaseVar(name="dict_var", type_=Dict[str, str]),
|
BaseVar(_var_name="dict_var", _var_type=Dict[str, str]),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
BaseVar(name="df", type_=DataFrame),
|
BaseVar(_var_name="df", _var_type=DataFrame),
|
||||||
[1, 2],
|
[1, 2],
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
BaseVar(name="df", type_=DataFrame),
|
BaseVar(_var_name="df", _var_type=DataFrame),
|
||||||
{"name": "dict"},
|
{"name": "dict"},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
BaseVar(name="df", type_=DataFrame),
|
BaseVar(_var_name="df", _var_type=DataFrame),
|
||||||
{"set"},
|
{"set"},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
BaseVar(name="df", type_=DataFrame),
|
BaseVar(_var_name="df", _var_type=DataFrame),
|
||||||
(
|
(
|
||||||
1,
|
1,
|
||||||
2,
|
2,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
BaseVar(name="df", type_=DataFrame),
|
BaseVar(_var_name="df", _var_type=DataFrame),
|
||||||
BaseVar(name="list_var", type_=List[int]),
|
BaseVar(_var_name="list_var", _var_type=List[int]),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
BaseVar(name="df", type_=DataFrame),
|
BaseVar(_var_name="df", _var_type=DataFrame),
|
||||||
BaseVar(name="set_var", type_=Set[str]),
|
BaseVar(_var_name="set_var", _var_type=Set[str]),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
BaseVar(name="df", type_=DataFrame),
|
BaseVar(_var_name="df", _var_type=DataFrame),
|
||||||
BaseVar(name="dict_var", type_=Dict[str, str]),
|
BaseVar(_var_name="dict_var", _var_type=Dict[str, str]),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@ -577,8 +602,7 @@ def test_computed_var_with_annotation_error(request, fixture, full_name):
|
|||||||
state.var_with_annotation.foo
|
state.var_with_annotation.foo
|
||||||
assert (
|
assert (
|
||||||
err.value.args[0]
|
err.value.args[0]
|
||||||
== f"The State var `{full_name}` has no attribute 'foo' or may have been annotated wrongly.\n"
|
== f"The State var `{full_name}` has no attribute 'foo' or may have been annotated wrongly."
|
||||||
f"original message: 'ComputedVar' object has no attribute 'foo'"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -605,16 +629,19 @@ def test_import_var(import_var, expected):
|
|||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"key, expected",
|
"key, expected",
|
||||||
[
|
[
|
||||||
("test_key", BaseVar(name="localStorage.getItem('test_key')", type_=str)),
|
|
||||||
(
|
(
|
||||||
BaseVar(name="key_var", type_=str),
|
"test_key",
|
||||||
BaseVar(name="localStorage.getItem(key_var)", type_=str),
|
BaseVar(_var_name="localStorage.getItem('test_key')", _var_type=str),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
BaseVar(_var_name="key_var", _var_type=str),
|
||||||
|
BaseVar(_var_name="localStorage.getItem(key_var)", _var_type=str),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
BaseState.val,
|
BaseState.val,
|
||||||
BaseVar(name="localStorage.getItem(base_state.val)", type_=str),
|
BaseVar(_var_name="localStorage.getItem(base_state.val)", _var_type=str),
|
||||||
),
|
),
|
||||||
(None, BaseVar(name="getAllLocalStorageItems()", type_=Dict)),
|
(None, BaseVar(_var_name="getAllLocalStorageItems()", _var_type=Dict)),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_get_local_storage(key, expected):
|
def test_get_local_storage(key, expected):
|
||||||
@ -626,8 +653,8 @@ def test_get_local_storage(key, expected):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
local_storage = get_local_storage(key)
|
local_storage = get_local_storage(key)
|
||||||
assert local_storage.name == expected.name
|
assert local_storage._var_name == expected._var_name
|
||||||
assert local_storage.type_ == expected.type_
|
assert local_storage._var_type == expected._var_type
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
@ -636,8 +663,8 @@ def test_get_local_storage(key, expected):
|
|||||||
["list", "values"],
|
["list", "values"],
|
||||||
{"name": "dict"},
|
{"name": "dict"},
|
||||||
10,
|
10,
|
||||||
BaseVar(name="key_var", type_=List),
|
BaseVar(_var_name="key_var", _var_type=List),
|
||||||
BaseVar(name="key_var", type_=Dict[str, str]),
|
BaseVar(_var_name="key_var", _var_type=Dict[str, str]),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_get_local_storage_raise_error(key):
|
def test_get_local_storage_raise_error(key):
|
||||||
@ -648,7 +675,7 @@ def test_get_local_storage_raise_error(key):
|
|||||||
"""
|
"""
|
||||||
with pytest.raises(TypeError) as err:
|
with pytest.raises(TypeError) as err:
|
||||||
get_local_storage(key)
|
get_local_storage(key)
|
||||||
type_ = type(key) if not isinstance(key, Var) else key.type_
|
type_ = type(key) if not isinstance(key, Var) else key._var_type
|
||||||
assert (
|
assert (
|
||||||
err.value.args[0]
|
err.value.args[0]
|
||||||
== f"Local storage keys can only be of type `str` or `var` of type `str`. Got `{type_}` instead."
|
== f"Local storage keys can only be of type `str` or `var` of type `str`. Got `{type_}` instead."
|
||||||
@ -658,13 +685,13 @@ def test_get_local_storage_raise_error(key):
|
|||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"out, expected",
|
"out, expected",
|
||||||
[
|
[
|
||||||
(f"{BaseVar(name='var', type_=str)}", "${var}"),
|
(f"{BaseVar(_var_name='var', _var_type=str)}", "${var}"),
|
||||||
(
|
(
|
||||||
f"testing f-string with {BaseVar(name='myvar', state='state', type_=int)}",
|
f"testing f-string with {BaseVar(_var_name='myvar', _var_state='state', _var_type=int)}",
|
||||||
"testing f-string with ${state.myvar}",
|
"testing f-string with ${state.myvar}",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
f"testing local f-string {BaseVar(name='x', is_local=True, type_=str)}",
|
f"testing local f-string {BaseVar(_var_name='x', _var_is_local=True, _var_type=str)}",
|
||||||
"testing local f-string x",
|
"testing local f-string x",
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -676,14 +703,14 @@ def test_fstrings(out, expected):
|
|||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"var",
|
"var",
|
||||||
[
|
[
|
||||||
BaseVar(name="var", type_=int),
|
BaseVar(_var_name="var", _var_type=int),
|
||||||
BaseVar(name="var", type_=float),
|
BaseVar(_var_name="var", _var_type=float),
|
||||||
BaseVar(name="var", type_=str),
|
BaseVar(_var_name="var", _var_type=str),
|
||||||
BaseVar(name="var", type_=bool),
|
BaseVar(_var_name="var", _var_type=bool),
|
||||||
BaseVar(name="var", type_=dict),
|
BaseVar(_var_name="var", _var_type=dict),
|
||||||
BaseVar(name="var", type_=tuple),
|
BaseVar(_var_name="var", _var_type=tuple),
|
||||||
BaseVar(name="var", type_=set),
|
BaseVar(_var_name="var", _var_type=set),
|
||||||
BaseVar(name="var", type_=None),
|
BaseVar(_var_name="var", _var_type=None),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_unsupported_types_for_reverse(var):
|
def test_unsupported_types_for_reverse(var):
|
||||||
@ -700,11 +727,11 @@ def test_unsupported_types_for_reverse(var):
|
|||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"var",
|
"var",
|
||||||
[
|
[
|
||||||
BaseVar(name="var", type_=int),
|
BaseVar(_var_name="var", _var_type=int),
|
||||||
BaseVar(name="var", type_=float),
|
BaseVar(_var_name="var", _var_type=float),
|
||||||
BaseVar(name="var", type_=bool),
|
BaseVar(_var_name="var", _var_type=bool),
|
||||||
BaseVar(name="var", type_=set),
|
BaseVar(_var_name="var", _var_type=set),
|
||||||
BaseVar(name="var", type_=None),
|
BaseVar(_var_name="var", _var_type=None),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_unsupported_types_for_contains(var):
|
def test_unsupported_types_for_contains(var):
|
||||||
@ -717,34 +744,34 @@ def test_unsupported_types_for_contains(var):
|
|||||||
assert var.contains(1)
|
assert var.contains(1)
|
||||||
assert (
|
assert (
|
||||||
err.value.args[0]
|
err.value.args[0]
|
||||||
== f"Var var of type {var.type_} does not support contains check."
|
== f"Var var of type {var._var_type} does not support contains check."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"other",
|
"other",
|
||||||
[
|
[
|
||||||
BaseVar(name="other", type_=int),
|
BaseVar(_var_name="other", _var_type=int),
|
||||||
BaseVar(name="other", type_=float),
|
BaseVar(_var_name="other", _var_type=float),
|
||||||
BaseVar(name="other", type_=bool),
|
BaseVar(_var_name="other", _var_type=bool),
|
||||||
BaseVar(name="other", type_=list),
|
BaseVar(_var_name="other", _var_type=list),
|
||||||
BaseVar(name="other", type_=dict),
|
BaseVar(_var_name="other", _var_type=dict),
|
||||||
BaseVar(name="other", type_=tuple),
|
BaseVar(_var_name="other", _var_type=tuple),
|
||||||
BaseVar(name="other", type_=set),
|
BaseVar(_var_name="other", _var_type=set),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_unsupported_types_for_string_contains(other):
|
def test_unsupported_types_for_string_contains(other):
|
||||||
with pytest.raises(TypeError) as err:
|
with pytest.raises(TypeError) as err:
|
||||||
assert BaseVar(name="var", type_=str).contains(other)
|
assert BaseVar(_var_name="var", _var_type=str).contains(other)
|
||||||
assert (
|
assert (
|
||||||
err.value.args[0]
|
err.value.args[0]
|
||||||
== f"'in <string>' requires string as left operand, not {other.type_}"
|
== f"'in <string>' requires string as left operand, not {other._var_type}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_unsupported_default_contains():
|
def test_unsupported_default_contains():
|
||||||
with pytest.raises(TypeError) as err:
|
with pytest.raises(TypeError) as err:
|
||||||
assert 1 in BaseVar(name="var", type_=str)
|
assert 1 in BaseVar(_var_name="var", _var_type=str)
|
||||||
assert (
|
assert (
|
||||||
err.value.args[0]
|
err.value.args[0]
|
||||||
== "'in' operator not supported for Var types, use Var.contains() instead."
|
== "'in' operator not supported for Var types, use Var.contains() instead."
|
||||||
|
@ -211,11 +211,11 @@ def test_format_string(input: str, output: str):
|
|||||||
"input,output",
|
"input,output",
|
||||||
[
|
[
|
||||||
(Var.create(value="test"), "{`test`}"),
|
(Var.create(value="test"), "{`test`}"),
|
||||||
(Var.create(value="test", is_local=True), "{`test`}"),
|
(Var.create(value="test", _var_is_local=True), "{`test`}"),
|
||||||
(Var.create(value="test", is_local=False), "{test}"),
|
(Var.create(value="test", _var_is_local=False), "{test}"),
|
||||||
(Var.create(value="test", is_string=True), "{`test`}"),
|
(Var.create(value="test", _var_is_string=True), "{`test`}"),
|
||||||
(Var.create(value="test", is_string=False), "{`test`}"),
|
(Var.create(value="test", _var_is_string=False), "{`test`}"),
|
||||||
(Var.create(value="test", is_local=False, is_string=False), "{test}"),
|
(Var.create(value="test", _var_is_local=False, _var_is_string=False), "{test}"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_format_var(input: Var, output: str):
|
def test_format_var(input: Var, output: str):
|
||||||
@ -285,7 +285,7 @@ def test_format_cond(condition: str, true_value: str, false_value: str, expected
|
|||||||
(
|
(
|
||||||
{
|
{
|
||||||
"a": 'foo "{ "bar" }" baz',
|
"a": 'foo "{ "bar" }" baz',
|
||||||
"b": BaseVar(name="val", type_="str"),
|
"b": BaseVar(_var_name="val", _var_type="str"),
|
||||||
},
|
},
|
||||||
r'{{"a": "foo \"{ \"bar\" }\" baz", "b": val}}',
|
r'{{"a": "foo \"{ \"bar\" }\" baz", "b": val}}',
|
||||||
),
|
),
|
||||||
@ -308,22 +308,25 @@ def test_format_cond(condition: str, true_value: str, false_value: str, expected
|
|||||||
'{_e => addEvents([Event("mock_event", {arg:_e.target.value})], _e)}',
|
'{_e => addEvents([Event("mock_event", {arg:_e.target.value})], _e)}',
|
||||||
),
|
),
|
||||||
({"a": "red", "b": "blue"}, '{{"a": "red", "b": "blue"}}'),
|
({"a": "red", "b": "blue"}, '{{"a": "red", "b": "blue"}}'),
|
||||||
(BaseVar(name="var", type_="int"), "{var}"),
|
(BaseVar(_var_name="var", _var_type="int"), "{var}"),
|
||||||
(
|
(
|
||||||
BaseVar(
|
BaseVar(
|
||||||
name="_",
|
_var_name="_",
|
||||||
type_=Any,
|
_var_type=Any,
|
||||||
state="",
|
_var_state="",
|
||||||
is_local=True,
|
_var_is_local=True,
|
||||||
is_string=False,
|
_var_is_string=False,
|
||||||
),
|
),
|
||||||
"{_}",
|
"{_}",
|
||||||
),
|
),
|
||||||
(BaseVar(name='state.colors["a"]', type_="str"), '{state.colors["a"]}'),
|
|
||||||
({"a": BaseVar(name="val", type_="str")}, '{{"a": val}}'),
|
|
||||||
({"a": BaseVar(name='"val"', type_="str")}, '{{"a": "val"}}'),
|
|
||||||
(
|
(
|
||||||
{"a": BaseVar(name='state.colors["val"]', type_="str")},
|
BaseVar(_var_name='state.colors["a"]', _var_type="str"),
|
||||||
|
'{state.colors["a"]}',
|
||||||
|
),
|
||||||
|
({"a": BaseVar(_var_name="val", _var_type="str")}, '{{"a": val}}'),
|
||||||
|
({"a": BaseVar(_var_name='"val"', _var_type="str")}, '{{"a": "val"}}'),
|
||||||
|
(
|
||||||
|
{"a": BaseVar(_var_name='state.colors["val"]', _var_type="str")},
|
||||||
'{{"a": state.colors["val"]}}',
|
'{{"a": state.colors["val"]}}',
|
||||||
),
|
),
|
||||||
# tricky real-world case from markdown component
|
# tricky real-world case from markdown component
|
||||||
|
@ -105,16 +105,16 @@ def test_add_serializer():
|
|||||||
([1, 2, 3], "[1, 2, 3]"),
|
([1, 2, 3], "[1, 2, 3]"),
|
||||||
([1, "2", 3.0], '[1, "2", 3.0]'),
|
([1, "2", 3.0], '[1, "2", 3.0]'),
|
||||||
(
|
(
|
||||||
[1, Var.create_safe("hi"), Var.create_safe("bye", is_local=False)],
|
[1, Var.create_safe("hi"), Var.create_safe("bye", _var_is_local=False)],
|
||||||
'[1, "hi", bye]',
|
'[1, "hi", bye]',
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
(1, Var.create_safe("hi"), Var.create_safe("bye", is_local=False)),
|
(1, Var.create_safe("hi"), Var.create_safe("bye", _var_is_local=False)),
|
||||||
'[1, "hi", bye]',
|
'[1, "hi", bye]',
|
||||||
),
|
),
|
||||||
({1: 2, 3: 4}, '{"1": 2, "3": 4}'),
|
({1: 2, 3: 4}, '{"1": 2, "3": 4}'),
|
||||||
(
|
(
|
||||||
{1: Var.create_safe("hi"), 3: Var.create_safe("bye", is_local=False)},
|
{1: Var.create_safe("hi"), 3: Var.create_safe("bye", _var_is_local=False)},
|
||||||
'{"1": "hi", "3": bye}',
|
'{"1": "hi", "3": bye}',
|
||||||
),
|
),
|
||||||
(datetime.datetime(2021, 1, 1, 1, 1, 1, 1), "2021-01-01 01:01:01.000001"),
|
(datetime.datetime(2021, 1, 1, 1, 1, 1, 1), "2021-01-01 01:01:01.000001"),
|
||||||
|
Loading…
Reference in New Issue
Block a user