reflex 0.6.0__py3-none-any.whl → 0.6.0a1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of reflex might be problematic. Click here for more details.
- reflex/.templates/jinja/custom_components/pyproject.toml.jinja2 +2 -2
- reflex/.templates/jinja/web/pages/_app.js.jinja2 +1 -1
- reflex/.templates/jinja/web/pages/utils.js.jinja2 +2 -2
- reflex/.templates/web/components/reflex/chakra_color_mode_provider.js +36 -0
- reflex/.templates/web/utils/state.js +1 -3
- reflex/__init__.py +2 -8
- reflex/__init__.pyi +1 -2
- reflex/app.py +2 -4
- reflex/app_module_for_backend.py +1 -1
- reflex/base.py +1 -1
- reflex/compiler/compiler.py +2 -2
- reflex/compiler/utils.py +3 -3
- reflex/components/base/app_wrap.py +2 -2
- reflex/components/base/app_wrap.pyi +27 -17
- reflex/components/base/bare.py +5 -4
- reflex/components/base/body.pyi +27 -17
- reflex/components/base/document.pyi +131 -81
- reflex/components/base/error_boundary.py +7 -6
- reflex/components/base/error_boundary.pyi +33 -20
- reflex/components/base/fragment.pyi +27 -17
- reflex/components/base/head.pyi +53 -33
- reflex/components/base/link.py +1 -1
- reflex/components/base/link.pyi +54 -33
- reflex/components/base/meta.pyi +105 -65
- reflex/components/base/script.py +2 -1
- reflex/components/base/script.pyi +38 -21
- reflex/components/component.py +47 -53
- reflex/components/core/banner.py +27 -23
- reflex/components/core/banner.pyi +171 -134
- reflex/components/core/client_side_routing.py +3 -2
- reflex/components/core/client_side_routing.pyi +54 -33
- reflex/components/core/clipboard.py +1 -2
- reflex/components/core/clipboard.pyi +33 -20
- reflex/components/core/cond.py +5 -5
- reflex/components/core/debounce.py +5 -5
- reflex/components/core/debounce.pyi +33 -20
- reflex/components/core/foreach.py +4 -3
- reflex/components/core/html.py +1 -1
- reflex/components/core/html.pyi +46 -35
- reflex/components/core/match.py +17 -17
- reflex/components/core/upload.py +23 -17
- reflex/components/core/upload.pyi +124 -78
- reflex/components/datadisplay/code.py +10 -9
- reflex/components/datadisplay/code.pyi +409 -299
- reflex/components/datadisplay/dataeditor.py +10 -8
- reflex/components/datadisplay/dataeditor.pyi +53 -40
- reflex/components/el/element.pyi +27 -17
- reflex/components/el/elements/base.py +1 -1
- reflex/components/el/elements/base.pyi +45 -34
- reflex/components/el/elements/forms.py +16 -16
- reflex/components/el/elements/forms.pyi +707 -554
- reflex/components/el/elements/inline.py +1 -1
- reflex/components/el/elements/inline.pyi +1218 -937
- reflex/components/el/elements/media.py +1 -1
- reflex/components/el/elements/media.pyi +997 -786
- reflex/components/el/elements/metadata.py +6 -3
- reflex/components/el/elements/metadata.pyi +242 -181
- reflex/components/el/elements/other.py +1 -1
- reflex/components/el/elements/other.pyi +306 -235
- reflex/components/el/elements/scripts.py +1 -1
- reflex/components/el/elements/scripts.pyi +140 -109
- reflex/components/el/elements/sectioning.py +2 -0
- reflex/components/el/elements/sectioning.pyi +647 -496
- reflex/components/el/elements/tables.py +1 -1
- reflex/components/el/elements/tables.pyi +452 -351
- reflex/components/el/elements/typography.py +1 -1
- reflex/components/el/elements/typography.pyi +657 -506
- reflex/components/gridjs/datatable.py +9 -6
- reflex/components/gridjs/datatable.pyi +56 -35
- reflex/components/lucide/icon.py +1 -1
- reflex/components/lucide/icon.pyi +54 -33
- reflex/components/markdown/markdown.py +31 -26
- reflex/components/markdown/markdown.pyi +37 -27
- reflex/components/moment/moment.py +12 -13
- reflex/components/moment/moment.pyi +35 -23
- reflex/components/next/base.pyi +27 -17
- reflex/components/next/image.py +1 -1
- reflex/components/next/image.pyi +37 -22
- reflex/components/next/link.py +1 -1
- reflex/components/next/link.pyi +28 -17
- reflex/components/next/video.py +1 -1
- reflex/components/next/video.pyi +28 -17
- reflex/components/plotly/plotly.py +13 -12
- reflex/components/plotly/plotly.pyi +54 -39
- reflex/components/props.py +1 -1
- reflex/components/radix/__init__.pyi +0 -1
- reflex/components/radix/primitives/__init__.pyi +0 -1
- reflex/components/radix/primitives/accordion.py +4 -4
- reflex/components/radix/primitives/accordion.pyi +495 -424
- reflex/components/radix/primitives/base.py +1 -1
- reflex/components/radix/primitives/base.pyi +54 -33
- reflex/components/radix/primitives/drawer.py +1 -1
- reflex/components/radix/primitives/drawer.pyi +273 -172
- reflex/components/radix/primitives/form.py +1 -1
- reflex/components/radix/primitives/form.pyi +364 -257
- reflex/components/radix/primitives/progress.py +1 -1
- reflex/components/radix/primitives/progress.pyi +282 -231
- reflex/components/radix/primitives/slider.py +1 -1
- reflex/components/radix/primitives/slider.pyi +138 -87
- reflex/components/radix/themes/base.py +24 -3
- reflex/components/radix/themes/base.pyi +250 -178
- reflex/components/radix/themes/color_mode.py +5 -5
- reflex/components/radix/themes/color_mode.pyi +220 -187
- reflex/components/radix/themes/components/alert_dialog.py +1 -1
- reflex/components/radix/themes/components/alert_dialog.pyi +207 -136
- reflex/components/radix/themes/components/aspect_ratio.py +1 -1
- reflex/components/radix/themes/components/aspect_ratio.pyi +28 -17
- reflex/components/radix/themes/components/avatar.py +1 -1
- reflex/components/radix/themes/components/avatar.pyi +81 -70
- reflex/components/radix/themes/components/badge.py +1 -1
- reflex/components/radix/themes/components/badge.pyi +99 -88
- reflex/components/radix/themes/components/button.py +1 -1
- reflex/components/radix/themes/components/button.pyi +109 -98
- reflex/components/radix/themes/components/callout.py +1 -1
- reflex/components/radix/themes/components/callout.pyi +373 -322
- reflex/components/radix/themes/components/card.py +1 -1
- reflex/components/radix/themes/components/card.pyi +49 -38
- reflex/components/radix/themes/components/checkbox.py +2 -1
- reflex/components/radix/themes/components/checkbox.pyi +245 -208
- reflex/components/radix/themes/components/checkbox_cards.py +1 -1
- reflex/components/radix/themes/components/checkbox_cards.pyi +115 -94
- reflex/components/radix/themes/components/checkbox_group.py +1 -1
- reflex/components/radix/themes/components/checkbox_group.pyi +107 -86
- reflex/components/radix/themes/components/context_menu.py +1 -1
- reflex/components/radix/themes/components/context_menu.pyi +319 -238
- reflex/components/radix/themes/components/data_list.py +1 -1
- reflex/components/radix/themes/components/data_list.pyi +171 -130
- reflex/components/radix/themes/components/dialog.py +1 -1
- reflex/components/radix/themes/components/dialog.pyi +210 -139
- reflex/components/radix/themes/components/dropdown_menu.py +1 -1
- reflex/components/radix/themes/components/dropdown_menu.pyi +332 -249
- reflex/components/radix/themes/components/hover_card.py +1 -1
- reflex/components/radix/themes/components/hover_card.pyi +131 -90
- reflex/components/radix/themes/components/icon_button.py +3 -2
- reflex/components/radix/themes/components/icon_button.pyi +109 -98
- reflex/components/radix/themes/components/inset.py +1 -1
- reflex/components/radix/themes/components/inset.pyi +58 -47
- reflex/components/radix/themes/components/popover.py +1 -1
- reflex/components/radix/themes/components/popover.pyi +136 -95
- reflex/components/radix/themes/components/progress.py +1 -1
- reflex/components/radix/themes/components/progress.pyi +82 -71
- reflex/components/radix/themes/components/radio.py +1 -1
- reflex/components/radix/themes/components/radio.pyi +80 -69
- reflex/components/radix/themes/components/radio_cards.py +1 -1
- reflex/components/radix/themes/components/radio_cards.pyi +119 -98
- reflex/components/radix/themes/components/radio_group.py +11 -8
- reflex/components/radix/themes/components/radio_group.pyi +271 -228
- reflex/components/radix/themes/components/scroll_area.py +1 -1
- reflex/components/radix/themes/components/scroll_area.pyi +32 -21
- reflex/components/radix/themes/components/segmented_control.py +1 -1
- reflex/components/radix/themes/components/segmented_control.pyi +113 -90
- reflex/components/radix/themes/components/select.py +3 -2
- reflex/components/radix/themes/components/select.pyi +471 -374
- reflex/components/radix/themes/components/separator.py +2 -1
- reflex/components/radix/themes/components/separator.pyi +80 -69
- reflex/components/radix/themes/components/skeleton.py +1 -1
- reflex/components/radix/themes/components/skeleton.pyi +34 -23
- reflex/components/radix/themes/components/slider.py +3 -2
- reflex/components/radix/themes/components/slider.pyi +88 -75
- reflex/components/radix/themes/components/spinner.py +1 -1
- reflex/components/radix/themes/components/spinner.pyi +30 -19
- reflex/components/radix/themes/components/switch.py +1 -1
- reflex/components/radix/themes/components/switch.pyi +84 -71
- reflex/components/radix/themes/components/table.py +1 -1
- reflex/components/radix/themes/components/table.pyi +332 -261
- reflex/components/radix/themes/components/tabs.py +1 -1
- reflex/components/radix/themes/components/tabs.pyi +194 -139
- reflex/components/radix/themes/components/text_area.py +1 -1
- reflex/components/radix/themes/components/text_area.pyi +111 -96
- reflex/components/radix/themes/components/text_field.py +1 -1
- reflex/components/radix/themes/components/text_field.pyi +286 -247
- reflex/components/radix/themes/components/tooltip.py +1 -1
- reflex/components/radix/themes/components/tooltip.pyi +37 -26
- reflex/components/radix/themes/layout/__init__.pyi +0 -1
- reflex/components/radix/themes/layout/base.py +1 -1
- reflex/components/radix/themes/layout/base.pyi +67 -56
- reflex/components/radix/themes/layout/box.pyi +45 -34
- reflex/components/radix/themes/layout/center.pyi +67 -56
- reflex/components/radix/themes/layout/container.py +2 -1
- reflex/components/radix/themes/layout/container.pyi +47 -36
- reflex/components/radix/themes/layout/flex.py +1 -1
- reflex/components/radix/themes/layout/flex.pyi +67 -56
- reflex/components/radix/themes/layout/grid.py +1 -1
- reflex/components/radix/themes/layout/grid.pyi +75 -64
- reflex/components/radix/themes/layout/list.py +6 -5
- reflex/components/radix/themes/layout/list.pyi +244 -193
- reflex/components/radix/themes/layout/section.py +2 -1
- reflex/components/radix/themes/layout/section.pyi +47 -36
- reflex/components/radix/themes/layout/spacer.pyi +67 -56
- reflex/components/radix/themes/layout/stack.py +1 -1
- reflex/components/radix/themes/layout/stack.pyi +159 -128
- reflex/components/radix/themes/typography/blockquote.py +1 -1
- reflex/components/radix/themes/typography/blockquote.pyi +100 -89
- reflex/components/radix/themes/typography/code.py +1 -1
- reflex/components/radix/themes/typography/code.pyi +101 -90
- reflex/components/radix/themes/typography/heading.py +1 -1
- reflex/components/radix/themes/typography/heading.pyi +107 -96
- reflex/components/radix/themes/typography/link.py +1 -1
- reflex/components/radix/themes/typography/link.pyi +113 -102
- reflex/components/radix/themes/typography/text.py +1 -1
- reflex/components/radix/themes/typography/text.pyi +572 -501
- reflex/components/react_player/audio.pyi +60 -33
- reflex/components/react_player/react_player.py +1 -1
- reflex/components/react_player/react_player.pyi +60 -33
- reflex/components/react_player/video.pyi +60 -33
- reflex/components/recharts/cartesian.py +3 -2
- reflex/components/recharts/cartesian.pyi +861 -678
- reflex/components/recharts/charts.py +5 -4
- reflex/components/recharts/charts.pyi +357 -252
- reflex/components/recharts/general.py +2 -1
- reflex/components/recharts/general.pyi +231 -180
- reflex/components/recharts/polar.py +5 -4
- reflex/components/recharts/polar.pyi +181 -144
- reflex/components/recharts/recharts.pyi +53 -33
- reflex/components/sonner/toast.py +17 -16
- reflex/components/sonner/toast.pyi +47 -36
- reflex/components/suneditor/editor.py +3 -2
- reflex/components/suneditor/editor.pyi +78 -55
- reflex/components/tags/cond_tag.py +4 -6
- reflex/components/tags/iter_tag.py +16 -28
- reflex/components/tags/match_tag.py +4 -6
- reflex/components/tags/tag.py +23 -40
- reflex/custom_components/custom_components.py +1 -3
- reflex/event.py +65 -113
- reflex/experimental/client_state.py +24 -25
- reflex/experimental/hooks.py +16 -16
- reflex/experimental/layout.py +5 -5
- reflex/experimental/layout.pyi +187 -136
- reflex/{vars → ivars}/__init__.py +2 -6
- reflex/{vars → ivars}/base.py +216 -599
- reflex/{vars → ivars}/function.py +19 -15
- reflex/{vars → ivars}/number.py +20 -41
- reflex/{vars → ivars}/object.py +30 -28
- reflex/{vars → ivars}/sequence.py +50 -53
- reflex/middleware/hydrate_middleware.py +0 -2
- reflex/middleware/middleware.py +3 -3
- reflex/state.py +82 -148
- reflex/style.py +22 -21
- reflex/utils/exceptions.py +0 -20
- reflex/utils/format.py +34 -54
- reflex/utils/imports.py +73 -16
- reflex/utils/prerequisites.py +15 -35
- reflex/utils/pyi_generator.py +8 -13
- reflex/utils/serializers.py +22 -12
- reflex/utils/telemetry.py +2 -3
- reflex/utils/types.py +5 -10
- reflex/vars.py +501 -0
- {reflex-0.6.0.dist-info → reflex-0.6.0a1.dist-info}/METADATA +5 -4
- reflex-0.6.0a1.dist-info/RECORD +384 -0
- reflex-0.6.0.dist-info/RECORD +0 -382
- {reflex-0.6.0.dist-info → reflex-0.6.0a1.dist-info}/LICENSE +0 -0
- {reflex-0.6.0.dist-info → reflex-0.6.0a1.dist-info}/WHEEL +0 -0
- {reflex-0.6.0.dist-info → reflex-0.6.0a1.dist-info}/entry_points.txt +0 -0
reflex/vars.py
ADDED
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
"""Define a state var."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import contextlib
|
|
6
|
+
import dataclasses
|
|
7
|
+
import random
|
|
8
|
+
import re
|
|
9
|
+
import string
|
|
10
|
+
from typing import (
|
|
11
|
+
TYPE_CHECKING,
|
|
12
|
+
Any,
|
|
13
|
+
Dict,
|
|
14
|
+
Iterable,
|
|
15
|
+
Optional,
|
|
16
|
+
Tuple,
|
|
17
|
+
Type,
|
|
18
|
+
_GenericAlias, # type: ignore
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
from reflex import constants
|
|
22
|
+
from reflex.utils import imports
|
|
23
|
+
from reflex.utils.exceptions import (
|
|
24
|
+
VarTypeError,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
# This module used to export ImportVar itself, so we still import it for export here
|
|
28
|
+
from reflex.utils.imports import (
|
|
29
|
+
ImmutableParsedImportDict,
|
|
30
|
+
ImportDict,
|
|
31
|
+
ImportVar,
|
|
32
|
+
ParsedImportDict,
|
|
33
|
+
parse_imports,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
if TYPE_CHECKING:
|
|
37
|
+
from reflex.ivars import ImmutableVar
|
|
38
|
+
from reflex.state import BaseState
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# Set of unique variable names.
|
|
42
|
+
USED_VARIABLES = set()
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# These names were changed in reflex 0.3.0
|
|
46
|
+
REPLACED_NAMES = {
|
|
47
|
+
"full_name": "_var_full_name",
|
|
48
|
+
"name": "_var_name",
|
|
49
|
+
"state": "_var_data.state",
|
|
50
|
+
"type_": "_var_type",
|
|
51
|
+
"is_local": "_var_is_local",
|
|
52
|
+
"is_string": "_var_is_string",
|
|
53
|
+
"set_state": "_var_set_state",
|
|
54
|
+
"deps": "_deps",
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def get_unique_variable_name() -> str:
|
|
59
|
+
"""Get a unique variable name.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
The unique variable name.
|
|
63
|
+
"""
|
|
64
|
+
name = "".join([random.choice(string.ascii_lowercase) for _ in range(8)])
|
|
65
|
+
if name not in USED_VARIABLES:
|
|
66
|
+
USED_VARIABLES.add(name)
|
|
67
|
+
return name
|
|
68
|
+
return get_unique_variable_name()
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@dataclasses.dataclass(
|
|
72
|
+
eq=True,
|
|
73
|
+
frozen=True,
|
|
74
|
+
)
|
|
75
|
+
class VarData:
|
|
76
|
+
"""Metadata associated with a Var."""
|
|
77
|
+
|
|
78
|
+
# The name of the enclosing state.
|
|
79
|
+
state: str = dataclasses.field(default="")
|
|
80
|
+
|
|
81
|
+
# Imports needed to render this var
|
|
82
|
+
imports: ImmutableParsedImportDict = dataclasses.field(default_factory=tuple)
|
|
83
|
+
|
|
84
|
+
# Hooks that need to be present in the component to render this var
|
|
85
|
+
hooks: Tuple[str, ...] = dataclasses.field(default_factory=tuple)
|
|
86
|
+
|
|
87
|
+
def __init__(
|
|
88
|
+
self,
|
|
89
|
+
state: str = "",
|
|
90
|
+
imports: ImportDict | ParsedImportDict | None = None,
|
|
91
|
+
hooks: dict[str, None] | None = None,
|
|
92
|
+
):
|
|
93
|
+
"""Initialize the var data.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
state: The name of the enclosing state.
|
|
97
|
+
imports: Imports needed to render this var.
|
|
98
|
+
hooks: Hooks that need to be present in the component to render this var.
|
|
99
|
+
"""
|
|
100
|
+
immutable_imports: ImmutableParsedImportDict = tuple(
|
|
101
|
+
sorted(
|
|
102
|
+
((k, tuple(sorted(v))) for k, v in parse_imports(imports or {}).items())
|
|
103
|
+
)
|
|
104
|
+
)
|
|
105
|
+
object.__setattr__(self, "state", state)
|
|
106
|
+
object.__setattr__(self, "imports", immutable_imports)
|
|
107
|
+
object.__setattr__(self, "hooks", tuple(hooks or {}))
|
|
108
|
+
|
|
109
|
+
def old_school_imports(self) -> ImportDict:
|
|
110
|
+
"""Return the imports as a mutable dict.
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
The imports as a mutable dict.
|
|
114
|
+
"""
|
|
115
|
+
return dict((k, list(v)) for k, v in self.imports)
|
|
116
|
+
|
|
117
|
+
@classmethod
|
|
118
|
+
def merge(cls, *others: VarData | None) -> VarData | None:
|
|
119
|
+
"""Merge multiple var data objects.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
*others: The var data objects to merge.
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
The merged var data object.
|
|
126
|
+
"""
|
|
127
|
+
state = ""
|
|
128
|
+
_imports = {}
|
|
129
|
+
hooks = {}
|
|
130
|
+
for var_data in others:
|
|
131
|
+
if var_data is None:
|
|
132
|
+
continue
|
|
133
|
+
state = state or var_data.state
|
|
134
|
+
_imports = imports.merge_imports(_imports, var_data.imports)
|
|
135
|
+
hooks.update(
|
|
136
|
+
var_data.hooks
|
|
137
|
+
if isinstance(var_data.hooks, dict)
|
|
138
|
+
else {k: None for k in var_data.hooks}
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
if state or _imports or hooks:
|
|
142
|
+
return VarData(
|
|
143
|
+
state=state,
|
|
144
|
+
imports=_imports,
|
|
145
|
+
hooks=hooks,
|
|
146
|
+
)
|
|
147
|
+
return None
|
|
148
|
+
|
|
149
|
+
def __bool__(self) -> bool:
|
|
150
|
+
"""Check if the var data is non-empty.
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
True if any field is set to a non-default value.
|
|
154
|
+
"""
|
|
155
|
+
return bool(self.state or self.imports or self.hooks)
|
|
156
|
+
|
|
157
|
+
def __eq__(self, other: Any) -> bool:
|
|
158
|
+
"""Check if two var data objects are equal.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
other: The other var data object to compare.
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
True if all fields are equal and collapsed imports are equal.
|
|
165
|
+
"""
|
|
166
|
+
if not isinstance(other, VarData):
|
|
167
|
+
return False
|
|
168
|
+
|
|
169
|
+
# Don't compare interpolations - that's added in by the decoder, and
|
|
170
|
+
# not part of the vardata itself.
|
|
171
|
+
return (
|
|
172
|
+
self.state == other.state
|
|
173
|
+
and self.hooks
|
|
174
|
+
== (
|
|
175
|
+
other.hooks if isinstance(other, VarData) else tuple(other.hooks.keys())
|
|
176
|
+
)
|
|
177
|
+
and imports.collapse_imports(self.imports)
|
|
178
|
+
== imports.collapse_imports(other.imports)
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
@classmethod
|
|
182
|
+
def from_state(cls, state: Type[BaseState] | str) -> VarData:
|
|
183
|
+
"""Set the state of the var.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
state: The state to set or the full name of the state.
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
The var with the set state.
|
|
190
|
+
"""
|
|
191
|
+
from reflex.utils import format
|
|
192
|
+
|
|
193
|
+
state_name = state if isinstance(state, str) else state.get_full_name()
|
|
194
|
+
new_var_data = VarData(
|
|
195
|
+
state=state_name,
|
|
196
|
+
hooks={
|
|
197
|
+
"const {0} = useContext(StateContexts.{0})".format(
|
|
198
|
+
format.format_state_name(state_name)
|
|
199
|
+
): None
|
|
200
|
+
},
|
|
201
|
+
imports={
|
|
202
|
+
f"/{constants.Dirs.CONTEXTS_PATH}": [ImportVar(tag="StateContexts")],
|
|
203
|
+
"react": [ImportVar(tag="useContext")],
|
|
204
|
+
},
|
|
205
|
+
)
|
|
206
|
+
return new_var_data
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _decode_var_immutable(value: str) -> tuple[VarData | None, str]:
|
|
210
|
+
"""Decode the state name from a formatted var.
|
|
211
|
+
|
|
212
|
+
Args:
|
|
213
|
+
value: The value to extract the state name from.
|
|
214
|
+
|
|
215
|
+
Returns:
|
|
216
|
+
The extracted state name and the value without the state name.
|
|
217
|
+
"""
|
|
218
|
+
var_datas = []
|
|
219
|
+
if isinstance(value, str):
|
|
220
|
+
# fast path if there is no encoded VarData
|
|
221
|
+
if constants.REFLEX_VAR_OPENING_TAG not in value:
|
|
222
|
+
return None, value
|
|
223
|
+
|
|
224
|
+
offset = 0
|
|
225
|
+
|
|
226
|
+
# Find all tags.
|
|
227
|
+
while m := _decode_var_pattern.search(value):
|
|
228
|
+
start, end = m.span()
|
|
229
|
+
value = value[:start] + value[end:]
|
|
230
|
+
|
|
231
|
+
serialized_data = m.group(1)
|
|
232
|
+
|
|
233
|
+
if serialized_data.isnumeric() or (
|
|
234
|
+
serialized_data[0] == "-" and serialized_data[1:].isnumeric()
|
|
235
|
+
):
|
|
236
|
+
# This is a global immutable var.
|
|
237
|
+
var = _global_vars[int(serialized_data)]
|
|
238
|
+
var_data = var._get_all_var_data()
|
|
239
|
+
|
|
240
|
+
if var_data is not None:
|
|
241
|
+
var_datas.append(var_data)
|
|
242
|
+
offset += end - start
|
|
243
|
+
|
|
244
|
+
return VarData.merge(*var_datas) if var_datas else None, value
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
# Compile regex for finding reflex var tags.
|
|
248
|
+
_decode_var_pattern_re = (
|
|
249
|
+
rf"{constants.REFLEX_VAR_OPENING_TAG}(.*?){constants.REFLEX_VAR_CLOSING_TAG}"
|
|
250
|
+
)
|
|
251
|
+
_decode_var_pattern = re.compile(_decode_var_pattern_re, flags=re.DOTALL)
|
|
252
|
+
|
|
253
|
+
# Defined global immutable vars.
|
|
254
|
+
_global_vars: Dict[int, ImmutableVar] = {}
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def _extract_var_data(value: Iterable) -> list[VarData | None]:
|
|
258
|
+
"""Extract the var imports and hooks from an iterable containing a Var.
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
value: The iterable to extract the VarData from
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
The extracted VarDatas.
|
|
265
|
+
"""
|
|
266
|
+
from reflex.ivars import ImmutableVar
|
|
267
|
+
from reflex.style import Style
|
|
268
|
+
|
|
269
|
+
var_datas = []
|
|
270
|
+
with contextlib.suppress(TypeError):
|
|
271
|
+
for sub in value:
|
|
272
|
+
if isinstance(sub, ImmutableVar):
|
|
273
|
+
var_datas.append(sub._var_data)
|
|
274
|
+
elif not isinstance(sub, str):
|
|
275
|
+
# Recurse into dict values.
|
|
276
|
+
if hasattr(sub, "values") and callable(sub.values):
|
|
277
|
+
var_datas.extend(_extract_var_data(sub.values()))
|
|
278
|
+
# Recurse into iterable values (or dict keys).
|
|
279
|
+
var_datas.extend(_extract_var_data(sub))
|
|
280
|
+
|
|
281
|
+
# Style objects should already have _var_data.
|
|
282
|
+
if isinstance(value, Style):
|
|
283
|
+
var_datas.append(value._var_data)
|
|
284
|
+
else:
|
|
285
|
+
# Recurse when value is a dict itself.
|
|
286
|
+
values = getattr(value, "values", None)
|
|
287
|
+
if callable(values):
|
|
288
|
+
var_datas.extend(_extract_var_data(values()))
|
|
289
|
+
return var_datas
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
class Var:
|
|
293
|
+
"""An abstract var."""
|
|
294
|
+
|
|
295
|
+
@classmethod
|
|
296
|
+
def create(
|
|
297
|
+
cls,
|
|
298
|
+
value: Any,
|
|
299
|
+
_var_is_local: bool = True,
|
|
300
|
+
_var_is_string: bool | None = None,
|
|
301
|
+
_var_data: Optional[VarData] = None,
|
|
302
|
+
) -> ImmutableVar | None:
|
|
303
|
+
"""Create a var from a value.
|
|
304
|
+
|
|
305
|
+
Args:
|
|
306
|
+
value: The value to create the var from.
|
|
307
|
+
_var_is_local: Whether the var is local.
|
|
308
|
+
_var_is_string: Whether the var is a string literal.
|
|
309
|
+
_var_data: Additional hooks and imports associated with the Var.
|
|
310
|
+
|
|
311
|
+
Returns:
|
|
312
|
+
The var.
|
|
313
|
+
"""
|
|
314
|
+
from reflex.ivars import ImmutableVar, LiteralVar
|
|
315
|
+
|
|
316
|
+
# Check for none values.
|
|
317
|
+
if value is None:
|
|
318
|
+
return None
|
|
319
|
+
|
|
320
|
+
# If the value is already a var, do nothing.
|
|
321
|
+
if isinstance(value, ImmutableVar):
|
|
322
|
+
return value
|
|
323
|
+
|
|
324
|
+
# If the value is not a string, create a LiteralVar.
|
|
325
|
+
if not isinstance(value, str):
|
|
326
|
+
return LiteralVar.create(
|
|
327
|
+
value,
|
|
328
|
+
_var_data=_var_data,
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
# if _var_is_string is provided, use that
|
|
332
|
+
if _var_is_string is False:
|
|
333
|
+
return ImmutableVar.create(
|
|
334
|
+
value,
|
|
335
|
+
_var_data=_var_data,
|
|
336
|
+
)
|
|
337
|
+
if _var_is_string is True:
|
|
338
|
+
return LiteralVar.create(
|
|
339
|
+
value,
|
|
340
|
+
_var_data=_var_data,
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
# Otherwise, rely on _var_is_local
|
|
344
|
+
if _var_is_local is True:
|
|
345
|
+
return LiteralVar.create(
|
|
346
|
+
value,
|
|
347
|
+
_var_data=_var_data,
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
return ImmutableVar.create(
|
|
351
|
+
value,
|
|
352
|
+
_var_data=_var_data,
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
@classmethod
|
|
356
|
+
def create_safe(
|
|
357
|
+
cls,
|
|
358
|
+
value: Any,
|
|
359
|
+
_var_is_local: bool = True,
|
|
360
|
+
_var_is_string: bool | None = None,
|
|
361
|
+
_var_data: Optional[VarData] = None,
|
|
362
|
+
) -> ImmutableVar:
|
|
363
|
+
"""Create a var from a value, asserting that it is not None.
|
|
364
|
+
|
|
365
|
+
Args:
|
|
366
|
+
value: The value to create the var from.
|
|
367
|
+
_var_is_local: Whether the var is local.
|
|
368
|
+
_var_is_string: Whether the var is a string literal.
|
|
369
|
+
_var_data: Additional hooks and imports associated with the Var.
|
|
370
|
+
|
|
371
|
+
Returns:
|
|
372
|
+
The var.
|
|
373
|
+
"""
|
|
374
|
+
var = cls.create(
|
|
375
|
+
value,
|
|
376
|
+
_var_is_local=_var_is_local,
|
|
377
|
+
_var_is_string=_var_is_string,
|
|
378
|
+
_var_data=_var_data,
|
|
379
|
+
)
|
|
380
|
+
assert var is not None
|
|
381
|
+
return var
|
|
382
|
+
|
|
383
|
+
@classmethod
|
|
384
|
+
def __class_getitem__(cls, type_: str) -> _GenericAlias:
|
|
385
|
+
"""Get a typed var.
|
|
386
|
+
|
|
387
|
+
Args:
|
|
388
|
+
type_: The type of the var.
|
|
389
|
+
|
|
390
|
+
Returns:
|
|
391
|
+
The var class item.
|
|
392
|
+
"""
|
|
393
|
+
return _GenericAlias(cls, type_)
|
|
394
|
+
|
|
395
|
+
def __bool__(self) -> bool:
|
|
396
|
+
"""Raise exception if using Var in a boolean context.
|
|
397
|
+
|
|
398
|
+
Raises:
|
|
399
|
+
VarTypeError: when attempting to bool-ify the Var.
|
|
400
|
+
"""
|
|
401
|
+
raise VarTypeError(
|
|
402
|
+
f"Cannot convert Var {str(self)!r} to bool for use with `if`, `and`, `or`, and `not`. "
|
|
403
|
+
"Instead use `rx.cond` and bitwise operators `&` (and), `|` (or), `~` (invert)."
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
def __iter__(self) -> Any:
|
|
407
|
+
"""Raise exception if using Var in an iterable context.
|
|
408
|
+
|
|
409
|
+
Raises:
|
|
410
|
+
VarTypeError: when attempting to iterate over the Var.
|
|
411
|
+
"""
|
|
412
|
+
raise VarTypeError(
|
|
413
|
+
f"Cannot iterate over Var {str(self)!r}. Instead use `rx.foreach`."
|
|
414
|
+
)
|
|
415
|
+
|
|
416
|
+
def __contains__(self, _: Any) -> ImmutableVar:
|
|
417
|
+
"""Override the 'in' operator to alert the user that it is not supported.
|
|
418
|
+
|
|
419
|
+
Raises:
|
|
420
|
+
VarTypeError: the operation is not supported
|
|
421
|
+
"""
|
|
422
|
+
raise VarTypeError(
|
|
423
|
+
"'in' operator not supported for Var types, use Var.contains() instead."
|
|
424
|
+
)
|
|
425
|
+
|
|
426
|
+
def __get__(self, instance: Any, owner: Any) -> ImmutableVar:
|
|
427
|
+
"""Return the value of the Var.
|
|
428
|
+
|
|
429
|
+
Args:
|
|
430
|
+
instance: The instance of the Var.
|
|
431
|
+
owner: The owner of the Var.
|
|
432
|
+
|
|
433
|
+
Returns:
|
|
434
|
+
The value of the Var.
|
|
435
|
+
"""
|
|
436
|
+
return self # type: ignore
|
|
437
|
+
|
|
438
|
+
@classmethod
|
|
439
|
+
def range(
|
|
440
|
+
cls,
|
|
441
|
+
v1: ImmutableVar | int = 0,
|
|
442
|
+
v2: ImmutableVar | int | None = None,
|
|
443
|
+
step: ImmutableVar | int | None = None,
|
|
444
|
+
) -> ImmutableVar:
|
|
445
|
+
"""Return an iterator over indices from v1 to v2 (or 0 to v1).
|
|
446
|
+
|
|
447
|
+
Args:
|
|
448
|
+
v1: The start of the range or end of range if v2 is not given.
|
|
449
|
+
v2: The end of the range.
|
|
450
|
+
step: The number of numbers between each item.
|
|
451
|
+
|
|
452
|
+
Returns:
|
|
453
|
+
A var representing range operation.
|
|
454
|
+
"""
|
|
455
|
+
from reflex.ivars import ArrayVar
|
|
456
|
+
|
|
457
|
+
return ArrayVar.range(v1, v2, step) # type: ignore
|
|
458
|
+
|
|
459
|
+
def upcast(self) -> ImmutableVar:
|
|
460
|
+
"""Upcast a Var to an ImmutableVar.
|
|
461
|
+
|
|
462
|
+
Returns:
|
|
463
|
+
The upcasted Var.
|
|
464
|
+
"""
|
|
465
|
+
return self # type: ignore
|
|
466
|
+
|
|
467
|
+
def json(self) -> str:
|
|
468
|
+
"""Serialize the var to a JSON string.
|
|
469
|
+
|
|
470
|
+
Raises:
|
|
471
|
+
NotImplementedError: If the method is not implemented.
|
|
472
|
+
"""
|
|
473
|
+
raise NotImplementedError("Var subclasses must implement the json method.")
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
def get_uuid_string_var() -> ImmutableVar:
|
|
477
|
+
"""Return a Var that generates a single memoized UUID via .web/utils/state.js.
|
|
478
|
+
|
|
479
|
+
useMemo with an empty dependency array ensures that the generated UUID is
|
|
480
|
+
consistent across re-renders of the component.
|
|
481
|
+
|
|
482
|
+
Returns:
|
|
483
|
+
A Var that generates a UUID at runtime.
|
|
484
|
+
"""
|
|
485
|
+
from reflex.ivars import ImmutableVar
|
|
486
|
+
from reflex.utils.imports import ImportVar
|
|
487
|
+
|
|
488
|
+
unique_uuid_var = get_unique_variable_name()
|
|
489
|
+
unique_uuid_var_data = VarData(
|
|
490
|
+
imports={
|
|
491
|
+
f"/{constants.Dirs.STATE_PATH}": {ImportVar(tag="generateUUID")}, # type: ignore
|
|
492
|
+
"react": "useMemo",
|
|
493
|
+
},
|
|
494
|
+
hooks={f"const {unique_uuid_var} = useMemo(generateUUID, [])": None},
|
|
495
|
+
)
|
|
496
|
+
|
|
497
|
+
return ImmutableVar(
|
|
498
|
+
_var_name=unique_uuid_var,
|
|
499
|
+
_var_type=str,
|
|
500
|
+
_var_data=unique_uuid_var_data,
|
|
501
|
+
)
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: reflex
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.0a1
|
|
4
4
|
Summary: Web apps in pure Python.
|
|
5
5
|
Home-page: https://reflex.dev
|
|
6
6
|
License: Apache-2.0
|
|
7
7
|
Keywords: web,framework
|
|
8
8
|
Author: Nikhil Rao
|
|
9
9
|
Author-email: nikhil@reflex.dev
|
|
10
|
-
Requires-Python: >=3.
|
|
10
|
+
Requires-Python: >=3.8,<4.0
|
|
11
11
|
Classifier: Development Status :: 4 - Beta
|
|
12
12
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
13
|
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
14
15
|
Classifier: Programming Language :: Python :: 3.9
|
|
15
16
|
Classifier: Programming Language :: Python :: 3.10
|
|
16
17
|
Classifier: Programming Language :: Python :: 3.11
|
|
@@ -33,7 +34,7 @@ Requires-Dist: python-engineio (!=4.6.0)
|
|
|
33
34
|
Requires-Dist: python-multipart (>=0.0.5,<0.1)
|
|
34
35
|
Requires-Dist: python-socketio (>=5.7.0,<6.0)
|
|
35
36
|
Requires-Dist: redis (>=4.3.5,<6.0)
|
|
36
|
-
Requires-Dist: reflex-chakra (>=0.6.
|
|
37
|
+
Requires-Dist: reflex-chakra (>=0.6.0a)
|
|
37
38
|
Requires-Dist: reflex-hosting-cli (>=0.1.2,<2.0)
|
|
38
39
|
Requires-Dist: rich (>=13.0.0,<14.0)
|
|
39
40
|
Requires-Dist: setuptools (>=69.1.1,<70.2)
|
|
@@ -87,7 +88,7 @@ See our [architecture page](https://reflex.dev/blog/2024-03-21-reflex-architectu
|
|
|
87
88
|
|
|
88
89
|
## ⚙️ Installation
|
|
89
90
|
|
|
90
|
-
Open a terminal and run (Requires Python 3.
|
|
91
|
+
Open a terminal and run (Requires Python 3.8+):
|
|
91
92
|
|
|
92
93
|
```bash
|
|
93
94
|
pip install reflex
|