Compare commits

...

4 Commits
main ... v0.5.4

Author SHA1 Message Date
Masen Furer
19ef2e645c
pyproject.toml: bump to 0.5.4 (#3480) 2024-06-13 10:42:42 -07:00
Masen Furer
f03ed7015f
compat: do not patch pydantic if the installed version starts with "1." (#3482)
In pydantic 1.10.16 (hopefully the last version), they added the `pydantic.v1`
namespace to make transitioning to the v2 package easier without changing code.

However, Reflex was depending on pydantic v1 NOT having `pydantic.v1` to skip
patching names from `v1` into the top level `pydantic` (normally we do this to
force sqlmodel to use v1, even when the v2 package is installed).

Unbreak CI
2024-06-12 07:52:14 -07:00
Masen Furer
f2168ee9c2
Override _var_is_string when handling str literals (#3473)
* Override _var_is_string when handling str literals

Maintain the pre 0.5.4 behavior when dealing with string literal
props, without displaying the deprecation warning.

Wait, isn't it weird to pass `_var_is_string=False` when the type is actually a str??

Yes, yes it is. However this is currently needed to avoid raising the
DeprecationWarning internally. These str-type vars with
_var_is_string set to false are handled by
`reflex.utils.format.format_prop`, but setting them to be
_var_is_string=True causes them to get quoted twice, which is not
what we want.

Var operations refactor will take care of cleaning this up, but for
now, we will go with the hack.

* Ignore type checks

Since I'm using an `isinstance` check now, the type checker thinks that `value`
could possibly be a string (instead of Any), which raises typing errors that
have to be ignored (they were ignored before implicitly due to being Any-typed)
2024-06-11 13:35:37 -07:00
Masen Furer
fdd7d9e0eb
Pass _var_is_string parameter to Var.create (#3470) 2024-06-11 13:35:36 -07:00
38 changed files with 177 additions and 93 deletions

View File

@ -1,6 +1,6 @@
[tool.poetry]
name = "reflex"
version = "0.5.3"
version = "0.5.4"
description = "Web apps in pure Python."
license = "Apache-2.0"
authors = [

View File

@ -65,7 +65,9 @@ class ChakraProvider(ChakraComponent):
A new ChakraProvider component.
"""
return super().create(
theme=Var.create("extendTheme(theme)", _var_is_local=False),
theme=Var.create(
"extendTheme(theme)", _var_is_local=False, _var_is_string=False
),
)
def _get_imports(self) -> imports.ImportDict:

View File

@ -51,7 +51,7 @@ class Checkbox(ChakraComponent):
name: Var[str]
# The value of the input field when checked (use is_checked prop for a bool)
value: Var[str] = Var.create("true") # type: ignore
value: Var[str] = Var.create("true", _var_is_string=True) # type: ignore
# The spacing between the checkbox and its label text (0.5rem)
spacing: Var[str]

View File

@ -122,7 +122,7 @@ class PinInput(ChakraComponent):
if ref:
return (
f"const {ref} = {str(refs_declaration)}; "
f"{str(Var.create_safe(ref).as_ref())} = {ref}"
f"{str(Var.create_safe(ref, _var_is_string=False).as_ref())} = {ref}"
)
return super()._get_ref_hook()

View File

@ -80,7 +80,7 @@ class RangeSlider(ChakraComponent):
if ref:
return (
f"const {ref} = Array.from({{length:2}}, () => useRef(null)); "
f"{str(Var.create_safe(ref).as_ref())} = {ref}"
f"{str(Var.create_safe(ref, _var_is_string=False).as_ref())} = {ref}"
)
return super()._get_ref_hook()

View File

@ -25,7 +25,9 @@ class Link(ChakraComponent):
text: Var[str]
# What the link renders to.
as_: Var[str] = BaseVar.create(value="{NextLink}", _var_is_local=False) # type: ignore
as_: Var[str] = BaseVar.create(
value="{NextLink}", _var_is_local=False, _var_is_string=False
) # type: ignore
# If true, the link will open in new tab.
is_external: Var[bool]

View File

@ -319,7 +319,9 @@ class Component(BaseComponent, ABC):
# Set default values for any props.
if types._issubclass(field.type_, Var):
field.required = False
field.default = Var.create(field.default)
field.default = Var.create(
field.default, _var_is_string=isinstance(field.default, str)
)
elif types._issubclass(field.type_, EventHandler):
field.required = False
@ -348,7 +350,10 @@ class Component(BaseComponent, ABC):
"id": kwargs.get("id"),
"children": children,
**{
prop: Var.create(kwargs[prop])
prop: Var.create(
kwargs[prop],
_var_is_string=False if isinstance(kwargs[prop], str) else None,
)
for prop in self.get_initial_props()
if prop in kwargs
},
@ -395,7 +400,10 @@ class Component(BaseComponent, ABC):
passed_types = None
try:
# Try to create a var from the value.
kwargs[key] = Var.create(value)
kwargs[key] = Var.create(
value,
_var_is_string=False if isinstance(value, str) else None,
)
# Check that the var type is not None.
if kwargs[key] is None:
@ -420,7 +428,9 @@ class Component(BaseComponent, ABC):
if types.is_union(passed_type):
# We need to check all possible types in the union.
passed_types = (
arg for arg in passed_type.__args__ if arg is not type(None)
arg
for arg in passed_type.__args__ # type: ignore
if arg is not type(None)
)
if (
# If the passed var is a union, check if all possible types are valid.
@ -442,7 +452,8 @@ class Component(BaseComponent, ABC):
if key in component_specific_triggers:
# Temporarily disable full control for event triggers.
kwargs["event_triggers"][key] = self._create_event_chain(
value=value, args_spec=component_specific_triggers[key]
value=value, # type: ignore
args_spec=component_specific_triggers[key],
)
# Remove any keys that were added as events.
@ -672,7 +683,9 @@ class Component(BaseComponent, ABC):
# Add ref to element if `id` is not None.
ref = self.get_ref()
if ref is not None:
props["ref"] = Var.create(ref, _var_is_local=False)
props["ref"] = Var.create(
ref, _var_is_local=False, _var_is_string=False
)
else:
props = props.copy()
@ -1091,7 +1104,9 @@ class Component(BaseComponent, ABC):
vars.append(comp_prop)
elif isinstance(comp_prop, str):
# Collapse VarData encoded in f-strings.
var = Var.create_safe(comp_prop)
var = Var.create_safe(
comp_prop, _var_is_string=isinstance(comp_prop, str)
)
if var._var_data is not None:
vars.append(var)
@ -1388,7 +1403,7 @@ class Component(BaseComponent, ABC):
"""
ref = self.get_ref()
if ref is not None:
return f"const {ref} = useRef(null); {str(Var.create_safe(ref).as_ref())} = {ref};"
return f"const {ref} = useRef(null); {str(Var.create_safe(ref, _var_is_string=False).as_ref())} = {ref};"
def _get_vars_hooks(self) -> dict[str, None]:
"""Get the hooks required by vars referenced in this component.
@ -2147,7 +2162,7 @@ class StatefulComponent(BaseComponent):
# Store the memoized function name and hook code for this event trigger.
trigger_memo[event_trigger] = (
Var.create_safe(memo_name)._replace(
Var.create_safe(memo_name, _var_is_string=False)._replace(
_var_type=EventChain, merge_var_data=memo_var_data
),
f"const {memo_name} = useCallback({rendered_chain}, [{', '.join(var_deps)}])",

View File

@ -132,7 +132,8 @@ useEffect(() => {{
toast.dismiss("{toast_id}");
setUserDismissed(false); // after reconnection reset dismissed state
}}
}}, [{connect_errors}]);"""
}}, [{connect_errors}]);""",
_var_is_string=False,
)
hook._var_data = VarData.merge( # type: ignore

View File

@ -14,7 +14,7 @@ from reflex.components.component import Component
from reflex.components.core.cond import cond
from reflex.vars import Var
route_not_found: Var = Var.create_safe(constants.ROUTE_NOT_FOUND)
route_not_found: Var = Var.create_safe(constants.ROUTE_NOT_FOUND, _var_is_string=False)
class ClientSideRouting(Component):

View File

@ -99,7 +99,9 @@ class DebounceInput(Component):
props["class_name"] = f"{props.get('class_name', '')} {child.class_name}"
child_ref = child.get_ref()
if props.get("input_ref") is None and child_ref:
props["input_ref"] = Var.create_safe(child_ref, _var_is_local=False)
props["input_ref"] = Var.create_safe(
child_ref, _var_is_local=False, _var_is_string=False
)
props["id"] = child.id
# Set the child element to wrap, including any imports/hooks from the child.

View File

@ -60,7 +60,7 @@ class Foreach(Component):
deprecation_version="0.5.0",
removal_version="0.6.0",
)
iterable = Var.create_safe(iterable)
iterable = Var.create_safe(iterable, _var_is_string=False)
if iterable._var_type == Any:
raise ForeachVarError(
f"Could not foreach over var `{iterable._var_full_name}` of type Any. "

View File

@ -119,6 +119,7 @@ def get_upload_dir() -> Path:
uploaded_files_url_prefix: Var = Var.create_safe(
"${getBackendURL(env.UPLOAD)}",
_var_is_string=False,
_var_data=VarData(
imports={
f"/{Dirs.STATE_PATH}": [imports.ImportVar(tag="getBackendURL")],

View File

@ -504,10 +504,13 @@ class CodeBlock(Component):
style=Var.create(
format.to_camel_case(f"{predicate}{qmark}{value.replace('`', '')}"),
_var_is_local=False,
_var_is_string=False,
)
).remove_props("theme", "code")
if self.code is not None:
out.special_props.add(Var.create_safe(f"children={str(self.code)}"))
out.special_props.add(
Var.create_safe(f"children={str(self.code)}", _var_is_string=False)
)
return out
@staticmethod

View File

@ -263,7 +263,9 @@ class DataEditor(NoSSRComponent):
# Define the name of the getData callback associated with this component and assign to get_cell_content.
data_callback = f"getData_{editor_id}"
self.get_cell_content = Var.create(data_callback, _var_is_local=False) # type: ignore
self.get_cell_content = Var.create(
data_callback, _var_is_local=False, _var_is_string=False
) # type: ignore
code = [f"function {data_callback}([col, row])" "{"]
@ -301,11 +303,7 @@ class DataEditor(NoSSRComponent):
# If rows is not provided, determine from data.
if rows is None:
props["rows"] = (
data.length() # BaseVar.create(value=f"{data}.length()", is_local=False)
if isinstance(data, Var)
else len(data)
)
props["rows"] = data.length() if isinstance(data, Var) else len(data)
if not isinstance(columns, Var) and len(columns):
if (

View File

@ -17,7 +17,7 @@ from reflex.vars import BaseVar, Var
from .base import BaseHTML
FORM_DATA = Var.create("form_data")
FORM_DATA = Var.create("form_data", _var_is_string=False)
HANDLE_SUBMIT_JS_JINJA2 = Environment().from_string(
"""
const handleSubmit_{{ handle_submit_unique_name }} = useCallback((ev) => {
@ -221,17 +221,19 @@ class Form(BaseHTML):
# when ref start with refs_ it's an array of refs, so we need different method
# to collect data
if ref.startswith("refs_"):
ref_var = Var.create_safe(ref[:-3]).as_ref()
ref_var = Var.create_safe(ref[:-3], _var_is_string=False).as_ref()
form_refs[ref[5:-3]] = Var.create_safe(
f"getRefValues({str(ref_var)})",
_var_is_local=False,
_var_is_string=False,
_var_data=ref_var._var_data,
)
else:
ref_var = Var.create_safe(ref).as_ref()
ref_var = Var.create_safe(ref, _var_is_string=False).as_ref()
form_refs[ref[4:]] = Var.create_safe(
f"getRefValue({str(ref_var)})",
_var_is_local=False,
_var_is_string=False,
_var_data=ref_var._var_data,
)
return form_refs
@ -630,6 +632,7 @@ class Textarea(BaseHTML):
on_key_down=Var.create_safe(
f"(e) => enterKeySubmitOnKeyDown(e, {self.enter_key_submit._var_name_unwrapped})",
_var_is_local=False,
_var_is_string=False,
_var_data=self.enter_key_submit._var_data,
)
)
@ -638,6 +641,7 @@ class Textarea(BaseHTML):
on_input=Var.create_safe(
f"(e) => autoHeightOnInput(e, {self.auto_height._var_name_unwrapped})",
_var_is_local=False,
_var_is_string=False,
_var_data=self.auto_height._var_data,
)
)

View File

@ -19,7 +19,7 @@ from reflex.utils.format import format_event_chain
from reflex.vars import BaseVar, Var
from .base import BaseHTML
FORM_DATA = Var.create("form_data")
FORM_DATA = Var.create("form_data", _var_is_string=False)
HANDLE_SUBMIT_JS_JINJA2 = Environment().from_string(
"\n const handleSubmit_{{ handle_submit_unique_name }} = useCallback((ev) => {\n const $form = ev.target\n ev.preventDefault()\n const {{ form_data }} = {...Object.fromEntries(new FormData($form).entries()), ...{{ field_ref_mapping }}}\n\n {{ on_submit_event_chain }}\n\n if ({{ reset_on_submit }}) {\n $form.reset()\n }\n })\n "
)

View File

@ -23,19 +23,23 @@ from reflex.utils.imports import ImportVar
from reflex.vars import Var
# Special vars used in the component map.
_CHILDREN = Var.create_safe("children", _var_is_local=False)
_PROPS = Var.create_safe("...props", _var_is_local=False)
_MOCK_ARG = Var.create_safe("")
_CHILDREN = Var.create_safe("children", _var_is_local=False, _var_is_string=False)
_PROPS = Var.create_safe("...props", _var_is_local=False, _var_is_string=False)
_MOCK_ARG = Var.create_safe("", _var_is_string=False)
# Special remark plugins.
_REMARK_MATH = Var.create_safe("remarkMath", _var_is_local=False)
_REMARK_GFM = Var.create_safe("remarkGfm", _var_is_local=False)
_REMARK_UNWRAP_IMAGES = Var.create_safe("remarkUnwrapImages", _var_is_local=False)
_REMARK_MATH = Var.create_safe("remarkMath", _var_is_local=False, _var_is_string=False)
_REMARK_GFM = Var.create_safe("remarkGfm", _var_is_local=False, _var_is_string=False)
_REMARK_UNWRAP_IMAGES = Var.create_safe(
"remarkUnwrapImages", _var_is_local=False, _var_is_string=False
)
_REMARK_PLUGINS = Var.create_safe([_REMARK_MATH, _REMARK_GFM, _REMARK_UNWRAP_IMAGES])
# Special rehype plugins.
_REHYPE_KATEX = Var.create_safe("rehypeKatex", _var_is_local=False)
_REHYPE_RAW = Var.create_safe("rehypeRaw", _var_is_local=False)
_REHYPE_KATEX = Var.create_safe(
"rehypeKatex", _var_is_local=False, _var_is_string=False
)
_REHYPE_RAW = Var.create_safe("rehypeRaw", _var_is_local=False, _var_is_string=False)
_REHYPE_PLUGINS = Var.create_safe([_REHYPE_KATEX, _REHYPE_RAW])
# These tags do NOT get props passed to them
@ -210,7 +214,9 @@ class Markdown(Component):
# If the children are set as a prop, don't pass them as children.
children_prop = props.pop("children", None)
if children_prop is not None:
special_props.add(Var.create_safe(f"children={str(children_prop)}"))
special_props.add(
Var.create_safe(f"children={str(children_prop)}", _var_is_string=False)
)
children = []
# Get the component.
@ -259,7 +265,7 @@ class Markdown(Component):
return inline ? (
{self.format_component("code")}
) : (
{self.format_component("codeblock", language=Var.create_safe("language", _var_is_local=False))}
{self.format_component("codeblock", language=Var.create_safe("language", _var_is_local=False, _var_is_string=False))}
);
}}}}""".replace("\n", " ")

View File

@ -26,15 +26,19 @@ from reflex.utils import imports, types
from reflex.utils.imports import ImportVar
from reflex.vars import Var
_CHILDREN = Var.create_safe("children", _var_is_local=False)
_PROPS = Var.create_safe("...props", _var_is_local=False)
_MOCK_ARG = Var.create_safe("")
_REMARK_MATH = Var.create_safe("remarkMath", _var_is_local=False)
_REMARK_GFM = Var.create_safe("remarkGfm", _var_is_local=False)
_REMARK_UNWRAP_IMAGES = Var.create_safe("remarkUnwrapImages", _var_is_local=False)
_CHILDREN = Var.create_safe("children", _var_is_local=False, _var_is_string=False)
_PROPS = Var.create_safe("...props", _var_is_local=False, _var_is_string=False)
_MOCK_ARG = Var.create_safe("", _var_is_string=False)
_REMARK_MATH = Var.create_safe("remarkMath", _var_is_local=False, _var_is_string=False)
_REMARK_GFM = Var.create_safe("remarkGfm", _var_is_local=False, _var_is_string=False)
_REMARK_UNWRAP_IMAGES = Var.create_safe(
"remarkUnwrapImages", _var_is_local=False, _var_is_string=False
)
_REMARK_PLUGINS = Var.create_safe([_REMARK_MATH, _REMARK_GFM, _REMARK_UNWRAP_IMAGES])
_REHYPE_KATEX = Var.create_safe("rehypeKatex", _var_is_local=False)
_REHYPE_RAW = Var.create_safe("rehypeRaw", _var_is_local=False)
_REHYPE_KATEX = Var.create_safe(
"rehypeKatex", _var_is_local=False, _var_is_string=False
)
_REHYPE_RAW = Var.create_safe("rehypeRaw", _var_is_local=False, _var_is_string=False)
_REHYPE_PLUGINS = Var.create_safe([_REHYPE_KATEX, _REHYPE_RAW])
NO_PROPS_TAGS = ("ul", "ol", "li")

View File

@ -29,7 +29,7 @@ def _event_data_signature(e0: Var) -> List[Any]:
Returns:
The event key extracted from the event data (if defined).
"""
return [Var.create_safe(f"{e0}?.event")]
return [Var.create_safe(f"{e0}?.event", _var_is_string=False)]
def _event_points_data_signature(e0: Var) -> List[Any]:
@ -42,9 +42,10 @@ def _event_points_data_signature(e0: Var) -> List[Any]:
The event data and the extracted points.
"""
return [
Var.create_safe(f"{e0}?.event"),
Var.create_safe(f"{e0}?.event", _var_is_string=False),
Var.create_safe(
f"extractPoints({e0}?.points)",
_var_is_string=False,
),
]
@ -277,11 +278,14 @@ const extractPoints = (points) => {
Var.create_safe(
f"{{...mergician({figure._var_name_unwrapped},"
f"{','.join(md._var_name_unwrapped for md in merge_dicts)})}}",
_var_is_string=False,
),
)
else:
# Spread the figure dict over props, nothing to merge.
tag.special_props.add(
Var.create_safe(f"{{...{figure._var_name_unwrapped}}}")
Var.create_safe(
f"{{...{figure._var_name_unwrapped}}}", _var_is_string=False
)
)
return tag

View File

@ -105,7 +105,7 @@ class AccordionRoot(AccordionComponent):
duration: Var[int] = Var.create_safe(DEFAULT_ANIMATION_DURATION)
# The easing function to use for the animation.
easing: Var[str] = Var.create_safe(DEFAULT_ANIMATION_EASING)
easing: Var[str] = Var.create_safe(DEFAULT_ANIMATION_EASING, _var_is_string=True)
# Whether to show divider lines between items.
show_dividers: Var[bool]

View File

@ -233,6 +233,7 @@ class Theme(RadixThemesComponent):
css=Var.create(
"{{...theme.styles.global[':root'], ...theme.styles.global.body}}",
_var_is_local=False,
_var_is_string=False,
),
)
return tag

View File

@ -73,7 +73,7 @@ position_map = {
# needed to inverse contains for find
def _find(const, var):
return Var.create_safe(const).contains(var)
return Var.create_safe(const, _var_is_string=False).contains(var)
def _set_var_default(props, position, prop, default1, default2=""):

View File

@ -130,7 +130,9 @@ class HighLevelCheckbox(RadixThemesComponent):
}
@classmethod
def create(cls, text: Var[str] = Var.create_safe(""), **props) -> Component:
def create(
cls, text: Var[str] = Var.create_safe("", _var_is_string=True), **props
) -> Component:
"""Create a checkbox with a label.
Args:

View File

@ -91,10 +91,10 @@ class HighLevelRadioGroup(RadixThemesComponent):
direction: Var[LiteralFlexDirection]
# The gap between the items of the radio group.
spacing: Var[LiteralSpacing] = Var.create_safe("2")
spacing: Var[LiteralSpacing] = Var.create_safe("2", _var_is_string=True)
# The size of the radio group.
size: Var[Literal["1", "2", "3"]] = Var.create_safe("2")
size: Var[Literal["1", "2", "3"]] = Var.create_safe("2", _var_is_string=True)
# The variant of the radio group
variant: Var[Literal["classic", "surface", "soft"]]
@ -151,11 +151,13 @@ class HighLevelRadioGroup(RadixThemesComponent):
default_value = Var.create(default_value, _var_is_string=True) # type: ignore
else:
default_value = (
Var.create(default_value).to_string()._replace(_var_is_local=False) # type: ignore
Var.create(default_value, _var_is_string=False)
.to_string() # type: ignore
._replace(_var_is_local=False)
)
def radio_group_item(value: str | Var) -> Component:
item_value = Var.create(value) # type: ignore
item_value = Var.create(value, _var_is_string=False) # type: ignore
item_value = rx.cond(
item_value._type() == str, # type: ignore
item_value,

View File

@ -17,7 +17,7 @@ class Separator(RadixThemesComponent):
tag = "Separator"
# The size of the select: "1" | "2" | "3" | "4"
size: Var[LiteralSeperatorSize] = Var.create_safe("4")
size: Var[LiteralSeperatorSize] = Var.create_safe("4", _var_is_string=True)
# The color of the select
color_scheme: Var[LiteralAccentColor]

View File

@ -21,7 +21,7 @@ class Container(elements.Div, RadixThemesComponent):
tag = "Container"
# The size of the container: "1" - "4" (default "3")
size: Var[LiteralContainerSize] = Var.create_safe("3")
size: Var[LiteralContainerSize] = Var.create_safe("3", _var_is_string=True)
@classmethod
def create(

View File

@ -17,7 +17,7 @@ class Section(elements.Section, RadixThemesComponent):
tag = "Section"
# The size of the section: "1" - "3" (default "2")
size: Var[LiteralSectionSize] = Var.create_safe("2")
size: Var[LiteralSectionSize] = Var.create_safe("2", _var_is_string=True)
section = Section.create

View File

@ -150,7 +150,7 @@ class BarChart(ChartBase):
alias = "RechartsBarChart"
# The gap between two bar categories, which can be a percent value or a fixed value. Percentage | Number
bar_category_gap: Var[Union[str, int]] = Var.create_safe("10%") # type: ignore
bar_category_gap: Var[Union[str, int]] = Var.create_safe("10%", _var_is_string=True) # type: ignore
# The gap between two bars in the same category, which can be a percent value or a fixed value. Percentage | Number
bar_gap: Var[Union[str, int]] = Var.create_safe(4) # type: ignore

View File

@ -28,7 +28,7 @@ LiteralPosition = Literal[
]
toast_ref = Var.create_safe("refs['__toast']")
toast_ref = Var.create_safe("refs['__toast']", _var_is_string=False)
class ToastAction(Base):
@ -65,7 +65,8 @@ def _toast_callback_signature(toast: Var) -> list[Var]:
"""
return [
Var.create_safe(
f"(() => {{let {{action, cancel, onDismiss, onAutoClose, ...rest}} = {toast}; return rest}})()"
f"(() => {{let {{action, cancel, onDismiss, onAutoClose, ...rest}} = {toast}; return rest}})()",
_var_is_string=False,
)
]
@ -179,7 +180,9 @@ class Toaster(Component):
visible_toasts: Var[int]
# the position of the toast
position: Var[LiteralPosition] = Var.create_safe("bottom-right")
position: Var[LiteralPosition] = Var.create_safe(
"bottom-right", _var_is_string=True
)
# whether to show the close button
close_button: Var[bool] = Var.create_safe(False)
@ -217,6 +220,7 @@ class Toaster(Component):
hook = Var.create_safe(
f"{toast_ref} = toast",
_var_is_local=True,
_var_is_string=False,
_var_data=VarData(
imports={
"/utils/state": [ImportVar(tag="refs")],

View File

@ -27,7 +27,7 @@ LiteralPosition = Literal[
"bottom-center",
"bottom-right",
]
toast_ref = Var.create_safe("refs['__toast']")
toast_ref = Var.create_safe("refs['__toast']", _var_is_string=False)
class ToastAction(Base):
label: str

View File

@ -41,7 +41,8 @@ class Tag(Base):
# Convert any props to vars.
if "props" in kwargs:
kwargs["props"] = {
name: Var.create(value) for name, value in kwargs["props"].items()
name: Var.create(value, _var_is_string=False)
for name, value in kwargs["props"].items()
}
super().__init__(*args, **kwargs)

View File

@ -186,7 +186,7 @@ class EventHandler(EventActionsMixin):
# Get the function args.
fn_args = inspect.getfullargspec(self.fn).args[1:]
fn_args = (Var.create_safe(arg) for arg in fn_args)
fn_args = (Var.create_safe(arg, _var_is_string=False) for arg in fn_args)
# Construct the payload.
values = []
@ -264,7 +264,7 @@ class EventSpec(EventActionsMixin):
# Get the remaining unfilled function args.
fn_args = inspect.getfullargspec(self.handler.fn).args[1 + len(self.args) :]
fn_args = (Var.create_safe(arg) for arg in fn_args)
fn_args = (Var.create_safe(arg, _var_is_string=False) for arg in fn_args)
# Construct the payload.
values = []
@ -389,13 +389,13 @@ class FileUpload(Base):
spec_args = [
(
Var.create_safe("files"),
Var.create_safe(f"filesById.{upload_id}")._replace(
_var_data=upload_files_context_var_data
),
Var.create_safe("files", _var_is_string=False),
Var.create_safe(
f"filesById.{upload_id}", _var_is_string=False
)._replace(_var_data=upload_files_context_var_data),
),
(
Var.create_safe("upload_id"),
Var.create_safe("upload_id", _var_is_string=False),
Var.create_safe(upload_id, _var_is_string=True),
),
]
@ -424,7 +424,7 @@ class FileUpload(Base):
formatted_chain = str(format.format_prop(on_upload_progress_chain))
spec_args.append(
(
Var.create_safe("on_upload_progress"),
Var.create_safe("on_upload_progress", _var_is_string=False),
BaseVar(
_var_name=formatted_chain.strip("{}"),
_var_type=EventChain,
@ -464,7 +464,10 @@ def server_side(name: str, sig: inspect.Signature, **kwargs) -> EventSpec:
return EventSpec(
handler=EventHandler(fn=fn),
args=tuple(
(Var.create_safe(k), Var.create_safe(v, _var_is_string=isinstance(v, str)))
(
Var.create_safe(k, _var_is_string=False),
Var.create_safe(v, _var_is_string=isinstance(v, str)),
)
for k, v in kwargs.items()
),
)

View File

@ -20,8 +20,10 @@ def const(name, value) -> Var:
The constant Var.
"""
if isinstance(name, list):
return Var.create_safe(f"const [{', '.join(name)}] = {value}")
return Var.create_safe(f"const {name} = {value}")
return Var.create_safe(
f"const [{', '.join(name)}] = {value}", _var_is_string=False
)
return Var.create_safe(f"const {name} = {value}", _var_is_string=False)
def useCallback(func, deps) -> Var:
@ -36,6 +38,7 @@ def useCallback(func, deps) -> Var:
"""
return Var.create_safe(
f"useCallback({func}, {deps})" if deps else f"useCallback({func})",
_var_is_string=False,
_var_data=VarData(imports=_compose_react_imports(["useCallback"])),
)
@ -51,6 +54,7 @@ def useContext(context) -> Var:
"""
return Var.create_safe(
f"useContext({context})",
_var_is_string=False,
_var_data=VarData(imports=_compose_react_imports(["useContext"])),
)
@ -66,6 +70,7 @@ def useRef(default) -> Var:
"""
return Var.create_safe(
f"useRef({default})",
_var_is_string=False,
_var_data=VarData(imports=_compose_react_imports(["useRef"])),
)
@ -84,6 +89,7 @@ def useState(var_name, default=None) -> Var:
[var_name, f"set{var_name.capitalize()}"],
Var.create_safe(
f"useState({default})",
_var_is_string=False,
_var_data=VarData(imports=_compose_react_imports(["useState"])),
),
)

View File

@ -52,7 +52,11 @@ class Sidebar(Box, MemoizationLeaf):
"""
sidebar: Component = self.children[-2] # type: ignore
spacer: Component = self.children[-1] # type: ignore
open = self.State.open if self.State else Var.create("open") # type: ignore
open = (
self.State.open # type: ignore
if self.State
else Var.create_safe("open", _var_is_string=False)
)
sidebar.style["display"] = spacer.style["display"] = cond(open, "block", "none")
return Style(
@ -167,7 +171,10 @@ class SidebarTrigger(Fragment):
if sidebar.State:
open, toggle = sidebar.State.open, sidebar.State.toggle # type: ignore
else:
open, toggle = Var.create("open"), call_script(Var.create("setOpen(!open)")) # type: ignore
open, toggle = (
Var.create_safe("open", _var_is_string=False),
call_script(Var.create_safe("setOpen(!open)", _var_is_string=False)),
)
trigger_props["left"] = cond(open, f"calc({sidebar_width} - 32px)", "0")

View File

@ -79,7 +79,7 @@ def convert_item(style_item: str | Var) -> tuple[str, VarData | None]:
return str(style_item), style_item._var_data
# Otherwise, convert to Var to collapse VarData encoded in f-string.
new_var = Var.create(style_item)
new_var = Var.create(style_item, _var_is_string=False)
if new_var is not None and new_var._var_data:
# The wrapped backtick is used to identify the Var for interpolation.
return f"`{str(new_var)}`", new_var._var_data
@ -204,7 +204,7 @@ class Style(dict):
value: The value to set.
"""
# Create a Var to collapse VarData encoded in f-string.
_var = Var.create(value)
_var = Var.create(value, _var_is_string=False)
if _var is not None:
# Carry the imports/hooks when setting a Var as a value.
self._var_data = VarData.merge(self._var_data, _var._var_data)

View File

@ -21,6 +21,11 @@ def pydantic_v1_patch():
try:
import pydantic.v1 # type: ignore
if pydantic.__version__.startswith("1."):
# pydantic v1 is already installed
yield
return
sys.modules["pydantic.fields"] = pydantic.v1.fields # type: ignore
sys.modules["pydantic.main"] = pydantic.v1.main # type: ignore
sys.modules["pydantic.errors"] = pydantic.v1.errors # type: ignore

View File

@ -916,4 +916,7 @@ def format_data_editor_cell(cell: Any):
Returns:
The formatted cell.
"""
return {"kind": Var.create(value="GridCellKind.Text"), "data": cell}
return {
"kind": Var.create(value="GridCellKind.Text", _var_is_string=False),
"data": cell,
}

View File

@ -535,7 +535,7 @@ class Var:
if other is None:
return self._replace()
if not isinstance(other, Var):
other = Var.create(other)
other = Var.create(other, _var_is_string=False)
return self._replace(
_var_name=f"{{...{self._var_name}, ...{other._var_name}}}" # type: ignore
)
@ -831,9 +831,9 @@ class Var:
from reflex.utils import format
if isinstance(other, str):
other = Var.create(json.dumps(other))
other = Var.create(json.dumps(other), _var_is_string=False)
else:
other = Var.create(other)
other = Var.create(other, _var_is_string=False)
type_ = type_ or self._var_type
@ -1416,7 +1416,7 @@ class Var:
if isinstance(other, str):
other = Var.create(json.dumps(other), _var_is_string=True)
elif not isinstance(other, Var):
other = Var.create(other)
other = Var.create(other, _var_is_string=False)
if types._issubclass(self._var_type, Dict):
return self._replace(
_var_name=f"{self._var_name}.{method}({other._var_full_name})",
@ -1520,7 +1520,11 @@ class Var:
if not types._issubclass(self._var_type, str):
raise VarTypeError(f"Cannot strip 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), _var_is_string=False)
if isinstance(other, str)
else other
)
return self._replace(
_var_name=f"{self._var_name}.replace(/^${other._var_full_name}|${other._var_full_name}$/g, '')",
@ -1543,7 +1547,11 @@ class Var:
if not types._issubclass(self._var_type, str):
raise VarTypeError(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), _var_is_string=False)
if isinstance(other, str)
else other
)
return self._replace(
_var_name=f"{self._var_name}.split({other._var_full_name})",
@ -1568,11 +1576,11 @@ class Var:
raise VarTypeError(f"Cannot join non-list var {self._var_full_name}.")
if other is None:
other = Var.create_safe('""')
other = Var.create_safe('""', _var_is_string=False)
if isinstance(other, str):
other = Var.create_safe(json.dumps(other))
other = Var.create_safe(json.dumps(other), _var_is_string=False)
else:
other = Var.create_safe(other)
other = Var.create_safe(other, _var_is_string=False)
return self._replace(
_var_name=f"{self._var_name}.join({other._var_full_name})",
@ -1641,7 +1649,7 @@ class Var:
if not isinstance(v2, Var):
v2 = Var.create(v2)
if v2 is None:
v2 = Var.create_safe("undefined")
v2 = Var.create_safe("undefined", _var_is_string=False)
elif v2._var_type != int:
raise VarTypeError(f"Cannot get range on non-int var {v2._var_full_name}.")