reflex 0.6.0a1__py3-none-any.whl → 0.6.0a2__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/web/pages/_app.js.jinja2 +1 -1
- reflex/.templates/jinja/web/pages/utils.js.jinja2 +2 -2
- reflex/__init__.py +8 -2
- reflex/__init__.pyi +2 -1
- reflex/app.py +4 -2
- 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 +17 -27
- reflex/components/base/bare.py +4 -5
- reflex/components/base/body.pyi +17 -27
- reflex/components/base/document.pyi +81 -131
- reflex/components/base/error_boundary.py +6 -7
- reflex/components/base/error_boundary.pyi +20 -33
- reflex/components/base/fragment.pyi +17 -27
- reflex/components/base/head.pyi +33 -53
- reflex/components/base/link.py +1 -1
- reflex/components/base/link.pyi +33 -54
- reflex/components/base/meta.pyi +65 -105
- reflex/components/base/script.py +1 -2
- reflex/components/base/script.pyi +21 -38
- reflex/components/component.py +48 -47
- reflex/components/core/banner.py +23 -27
- reflex/components/core/banner.pyi +134 -171
- reflex/components/core/client_side_routing.py +2 -3
- reflex/components/core/client_side_routing.pyi +33 -54
- reflex/components/core/clipboard.py +2 -1
- reflex/components/core/clipboard.pyi +20 -33
- reflex/components/core/cond.py +5 -5
- reflex/components/core/debounce.py +5 -5
- reflex/components/core/debounce.pyi +20 -33
- reflex/components/core/foreach.py +3 -4
- reflex/components/core/html.py +1 -1
- reflex/components/core/html.pyi +35 -46
- reflex/components/core/match.py +17 -17
- reflex/components/core/upload.py +17 -23
- reflex/components/core/upload.pyi +78 -124
- reflex/components/datadisplay/code.py +9 -10
- reflex/components/datadisplay/code.pyi +302 -412
- reflex/components/datadisplay/dataeditor.py +8 -10
- reflex/components/datadisplay/dataeditor.pyi +40 -53
- reflex/components/el/element.pyi +17 -27
- reflex/components/el/elements/base.py +1 -1
- reflex/components/el/elements/base.pyi +34 -45
- reflex/components/el/elements/forms.py +16 -16
- reflex/components/el/elements/forms.pyi +554 -707
- reflex/components/el/elements/inline.py +1 -1
- reflex/components/el/elements/inline.pyi +937 -1218
- reflex/components/el/elements/media.py +1 -1
- reflex/components/el/elements/media.pyi +786 -997
- reflex/components/el/elements/metadata.py +3 -6
- reflex/components/el/elements/metadata.pyi +181 -242
- reflex/components/el/elements/other.py +1 -1
- reflex/components/el/elements/other.pyi +235 -306
- reflex/components/el/elements/scripts.py +1 -1
- reflex/components/el/elements/scripts.pyi +109 -140
- reflex/components/el/elements/sectioning.py +0 -2
- reflex/components/el/elements/sectioning.pyi +496 -647
- reflex/components/el/elements/tables.py +1 -1
- reflex/components/el/elements/tables.pyi +351 -452
- reflex/components/el/elements/typography.py +1 -1
- reflex/components/el/elements/typography.pyi +506 -657
- reflex/components/gridjs/datatable.py +6 -9
- reflex/components/gridjs/datatable.pyi +35 -56
- reflex/components/lucide/icon.py +1 -1
- reflex/components/lucide/icon.pyi +33 -54
- reflex/components/markdown/markdown.py +26 -31
- reflex/components/markdown/markdown.pyi +27 -37
- reflex/components/moment/moment.py +13 -12
- reflex/components/moment/moment.pyi +23 -35
- reflex/components/next/base.pyi +17 -27
- reflex/components/next/image.py +1 -1
- reflex/components/next/image.pyi +22 -37
- reflex/components/next/link.py +1 -1
- reflex/components/next/link.pyi +17 -28
- reflex/components/next/video.py +1 -1
- reflex/components/next/video.pyi +17 -28
- reflex/components/plotly/plotly.py +12 -13
- reflex/components/plotly/plotly.pyi +39 -54
- reflex/components/props.py +1 -1
- reflex/components/radix/__init__.pyi +1 -0
- reflex/components/radix/primitives/__init__.pyi +1 -0
- reflex/components/radix/primitives/accordion.py +4 -4
- reflex/components/radix/primitives/accordion.pyi +424 -495
- reflex/components/radix/primitives/base.py +1 -1
- reflex/components/radix/primitives/base.pyi +33 -54
- reflex/components/radix/primitives/drawer.py +1 -1
- reflex/components/radix/primitives/drawer.pyi +172 -273
- reflex/components/radix/primitives/form.py +1 -1
- reflex/components/radix/primitives/form.pyi +257 -364
- reflex/components/radix/primitives/progress.py +1 -1
- reflex/components/radix/primitives/progress.pyi +231 -282
- reflex/components/radix/primitives/slider.py +1 -1
- reflex/components/radix/primitives/slider.pyi +87 -138
- reflex/components/radix/themes/base.py +3 -24
- reflex/components/radix/themes/base.pyi +178 -250
- reflex/components/radix/themes/color_mode.py +5 -5
- reflex/components/radix/themes/color_mode.pyi +187 -220
- reflex/components/radix/themes/components/alert_dialog.py +1 -1
- reflex/components/radix/themes/components/alert_dialog.pyi +136 -207
- reflex/components/radix/themes/components/aspect_ratio.py +1 -1
- reflex/components/radix/themes/components/aspect_ratio.pyi +17 -28
- reflex/components/radix/themes/components/avatar.py +1 -1
- reflex/components/radix/themes/components/avatar.pyi +70 -81
- reflex/components/radix/themes/components/badge.py +1 -1
- reflex/components/radix/themes/components/badge.pyi +88 -99
- reflex/components/radix/themes/components/button.py +1 -1
- reflex/components/radix/themes/components/button.pyi +98 -109
- reflex/components/radix/themes/components/callout.py +1 -1
- reflex/components/radix/themes/components/callout.pyi +322 -373
- reflex/components/radix/themes/components/card.py +1 -1
- reflex/components/radix/themes/components/card.pyi +38 -49
- reflex/components/radix/themes/components/checkbox.py +1 -2
- reflex/components/radix/themes/components/checkbox.pyi +208 -245
- reflex/components/radix/themes/components/checkbox_cards.py +1 -1
- reflex/components/radix/themes/components/checkbox_cards.pyi +94 -115
- reflex/components/radix/themes/components/checkbox_group.py +1 -1
- reflex/components/radix/themes/components/checkbox_group.pyi +86 -107
- reflex/components/radix/themes/components/context_menu.py +1 -1
- reflex/components/radix/themes/components/context_menu.pyi +238 -319
- reflex/components/radix/themes/components/data_list.py +1 -1
- reflex/components/radix/themes/components/data_list.pyi +130 -171
- reflex/components/radix/themes/components/dialog.py +1 -1
- reflex/components/radix/themes/components/dialog.pyi +139 -210
- reflex/components/radix/themes/components/dropdown_menu.py +1 -1
- reflex/components/radix/themes/components/dropdown_menu.pyi +249 -332
- reflex/components/radix/themes/components/hover_card.py +1 -1
- reflex/components/radix/themes/components/hover_card.pyi +90 -131
- reflex/components/radix/themes/components/icon_button.py +2 -3
- reflex/components/radix/themes/components/icon_button.pyi +98 -109
- reflex/components/radix/themes/components/inset.py +1 -1
- reflex/components/radix/themes/components/inset.pyi +47 -58
- reflex/components/radix/themes/components/popover.py +1 -1
- reflex/components/radix/themes/components/popover.pyi +95 -136
- reflex/components/radix/themes/components/progress.py +1 -1
- reflex/components/radix/themes/components/progress.pyi +71 -82
- reflex/components/radix/themes/components/radio.py +1 -1
- reflex/components/radix/themes/components/radio.pyi +69 -80
- reflex/components/radix/themes/components/radio_cards.py +1 -1
- reflex/components/radix/themes/components/radio_cards.pyi +98 -119
- reflex/components/radix/themes/components/radio_group.py +8 -11
- reflex/components/radix/themes/components/radio_group.pyi +228 -271
- reflex/components/radix/themes/components/scroll_area.py +1 -1
- reflex/components/radix/themes/components/scroll_area.pyi +21 -32
- reflex/components/radix/themes/components/segmented_control.py +1 -1
- reflex/components/radix/themes/components/segmented_control.pyi +90 -113
- reflex/components/radix/themes/components/select.py +2 -3
- reflex/components/radix/themes/components/select.pyi +374 -471
- reflex/components/radix/themes/components/separator.py +1 -2
- reflex/components/radix/themes/components/separator.pyi +69 -80
- reflex/components/radix/themes/components/skeleton.py +1 -1
- reflex/components/radix/themes/components/skeleton.pyi +23 -34
- reflex/components/radix/themes/components/slider.py +2 -3
- reflex/components/radix/themes/components/slider.pyi +75 -88
- reflex/components/radix/themes/components/spinner.py +1 -1
- reflex/components/radix/themes/components/spinner.pyi +19 -30
- reflex/components/radix/themes/components/switch.py +1 -1
- reflex/components/radix/themes/components/switch.pyi +71 -84
- reflex/components/radix/themes/components/table.py +1 -1
- reflex/components/radix/themes/components/table.pyi +261 -332
- reflex/components/radix/themes/components/tabs.py +1 -1
- reflex/components/radix/themes/components/tabs.pyi +139 -194
- reflex/components/radix/themes/components/text_area.py +1 -1
- reflex/components/radix/themes/components/text_area.pyi +96 -111
- reflex/components/radix/themes/components/text_field.py +1 -1
- reflex/components/radix/themes/components/text_field.pyi +247 -286
- reflex/components/radix/themes/components/tooltip.py +1 -1
- reflex/components/radix/themes/components/tooltip.pyi +26 -37
- reflex/components/radix/themes/layout/__init__.pyi +1 -0
- reflex/components/radix/themes/layout/base.py +1 -1
- reflex/components/radix/themes/layout/base.pyi +56 -67
- reflex/components/radix/themes/layout/box.pyi +34 -45
- reflex/components/radix/themes/layout/center.pyi +56 -67
- reflex/components/radix/themes/layout/container.py +1 -2
- reflex/components/radix/themes/layout/container.pyi +36 -47
- reflex/components/radix/themes/layout/flex.py +1 -1
- reflex/components/radix/themes/layout/flex.pyi +56 -67
- reflex/components/radix/themes/layout/grid.py +1 -1
- reflex/components/radix/themes/layout/grid.pyi +64 -75
- reflex/components/radix/themes/layout/list.py +5 -6
- reflex/components/radix/themes/layout/list.pyi +193 -244
- reflex/components/radix/themes/layout/section.py +1 -2
- reflex/components/radix/themes/layout/section.pyi +36 -47
- reflex/components/radix/themes/layout/spacer.pyi +56 -67
- reflex/components/radix/themes/layout/stack.py +1 -1
- reflex/components/radix/themes/layout/stack.pyi +128 -159
- reflex/components/radix/themes/typography/blockquote.py +1 -1
- reflex/components/radix/themes/typography/blockquote.pyi +89 -100
- reflex/components/radix/themes/typography/code.py +1 -1
- reflex/components/radix/themes/typography/code.pyi +90 -101
- reflex/components/radix/themes/typography/heading.py +1 -1
- reflex/components/radix/themes/typography/heading.pyi +96 -107
- reflex/components/radix/themes/typography/link.py +1 -1
- reflex/components/radix/themes/typography/link.pyi +102 -113
- reflex/components/radix/themes/typography/text.py +1 -1
- reflex/components/radix/themes/typography/text.pyi +501 -572
- reflex/components/react_player/audio.pyi +33 -60
- reflex/components/react_player/react_player.py +1 -1
- reflex/components/react_player/react_player.pyi +33 -60
- reflex/components/react_player/video.pyi +33 -60
- reflex/components/recharts/cartesian.py +2 -3
- reflex/components/recharts/cartesian.pyi +678 -861
- reflex/components/recharts/charts.py +4 -5
- reflex/components/recharts/charts.pyi +252 -357
- reflex/components/recharts/general.py +1 -2
- reflex/components/recharts/general.pyi +180 -231
- reflex/components/recharts/polar.py +4 -5
- reflex/components/recharts/polar.pyi +144 -181
- reflex/components/recharts/recharts.pyi +33 -53
- reflex/components/sonner/toast.py +16 -17
- reflex/components/sonner/toast.pyi +36 -47
- reflex/components/suneditor/editor.py +2 -3
- reflex/components/suneditor/editor.pyi +55 -78
- reflex/components/tags/cond_tag.py +6 -4
- reflex/components/tags/iter_tag.py +28 -16
- reflex/components/tags/match_tag.py +6 -4
- reflex/components/tags/tag.py +40 -23
- reflex/event.py +113 -65
- reflex/experimental/client_state.py +18 -18
- reflex/experimental/hooks.py +16 -16
- reflex/experimental/layout.py +5 -5
- reflex/experimental/layout.pyi +136 -187
- reflex/middleware/hydrate_middleware.py +2 -0
- reflex/middleware/middleware.py +3 -3
- reflex/state.py +148 -82
- reflex/style.py +21 -22
- reflex/utils/exceptions.py +16 -0
- reflex/utils/format.py +22 -34
- reflex/utils/imports.py +16 -73
- reflex/utils/prerequisites.py +15 -8
- reflex/utils/pyi_generator.py +13 -8
- reflex/utils/serializers.py +12 -22
- reflex/utils/telemetry.py +2 -1
- reflex/utils/types.py +10 -5
- reflex/{ivars → vars}/__init__.py +6 -2
- reflex/{ivars → vars}/base.py +567 -206
- reflex/{ivars → vars}/function.py +15 -19
- reflex/{ivars → vars}/number.py +16 -18
- reflex/{ivars → vars}/object.py +28 -30
- reflex/{ivars → vars}/sequence.py +53 -42
- {reflex-0.6.0a1.dist-info → reflex-0.6.0a2.dist-info}/METADATA +2 -2
- reflex-0.6.0a2.dist-info/RECORD +382 -0
- reflex/.templates/web/components/reflex/chakra_color_mode_provider.js +0 -36
- reflex/vars.py +0 -501
- reflex-0.6.0a1.dist-info/RECORD +0 -384
- {reflex-0.6.0a1.dist-info → reflex-0.6.0a2.dist-info}/LICENSE +0 -0
- {reflex-0.6.0a1.dist-info → reflex-0.6.0a2.dist-info}/WHEEL +0 -0
- {reflex-0.6.0a1.dist-info → reflex-0.6.0a2.dist-info}/entry_points.txt +0 -0
reflex/{ivars → vars}/base.py
RENAMED
|
@@ -9,6 +9,9 @@ import dis
|
|
|
9
9
|
import functools
|
|
10
10
|
import inspect
|
|
11
11
|
import json
|
|
12
|
+
import random
|
|
13
|
+
import re
|
|
14
|
+
import string
|
|
12
15
|
import sys
|
|
13
16
|
from types import CodeType, FunctionType
|
|
14
17
|
from typing import (
|
|
@@ -17,6 +20,7 @@ from typing import (
|
|
|
17
20
|
Callable,
|
|
18
21
|
Dict,
|
|
19
22
|
Generic,
|
|
23
|
+
Iterable,
|
|
20
24
|
List,
|
|
21
25
|
Literal,
|
|
22
26
|
NoReturn,
|
|
@@ -43,15 +47,14 @@ from reflex.utils.exceptions import (
|
|
|
43
47
|
VarValueError,
|
|
44
48
|
)
|
|
45
49
|
from reflex.utils.format import format_state_name
|
|
46
|
-
from reflex.utils.
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
_extract_var_data,
|
|
53
|
-
_global_vars,
|
|
50
|
+
from reflex.utils.imports import (
|
|
51
|
+
ImmutableParsedImportDict,
|
|
52
|
+
ImportDict,
|
|
53
|
+
ImportVar,
|
|
54
|
+
ParsedImportDict,
|
|
55
|
+
parse_imports,
|
|
54
56
|
)
|
|
57
|
+
from reflex.utils.types import GenericType, Self, get_origin
|
|
55
58
|
|
|
56
59
|
if TYPE_CHECKING:
|
|
57
60
|
from reflex.state import BaseState
|
|
@@ -67,19 +70,18 @@ if TYPE_CHECKING:
|
|
|
67
70
|
from .sequence import ArrayVar, StringVar, ToArrayOperation, ToStringOperation
|
|
68
71
|
|
|
69
72
|
|
|
70
|
-
VAR_TYPE = TypeVar("VAR_TYPE")
|
|
73
|
+
VAR_TYPE = TypeVar("VAR_TYPE", covariant=True)
|
|
71
74
|
|
|
72
75
|
|
|
73
76
|
@dataclasses.dataclass(
|
|
74
77
|
eq=False,
|
|
75
78
|
frozen=True,
|
|
76
|
-
**{"slots": True} if sys.version_info >= (3, 10) else {},
|
|
77
79
|
)
|
|
78
|
-
class
|
|
80
|
+
class Var(Generic[VAR_TYPE]):
|
|
79
81
|
"""Base class for immutable vars."""
|
|
80
82
|
|
|
81
83
|
# The name of the var.
|
|
82
|
-
|
|
84
|
+
_js_expr: str = dataclasses.field()
|
|
83
85
|
|
|
84
86
|
# The type of the var.
|
|
85
87
|
_var_type: types.GenericType = dataclasses.field(default=Any)
|
|
@@ -93,7 +95,7 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|
|
93
95
|
Returns:
|
|
94
96
|
The name of the var.
|
|
95
97
|
"""
|
|
96
|
-
return self.
|
|
98
|
+
return self._js_expr
|
|
97
99
|
|
|
98
100
|
@property
|
|
99
101
|
def _var_is_local(self) -> bool:
|
|
@@ -104,6 +106,16 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|
|
104
106
|
"""
|
|
105
107
|
return False
|
|
106
108
|
|
|
109
|
+
@property
|
|
110
|
+
@deprecated("Use `_js_expr` instead.")
|
|
111
|
+
def _var_name(self) -> str:
|
|
112
|
+
"""The name of the var.
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
The name of the var.
|
|
116
|
+
"""
|
|
117
|
+
return self._js_expr
|
|
118
|
+
|
|
107
119
|
@property
|
|
108
120
|
def _var_is_string(self) -> bool:
|
|
109
121
|
"""Whether the var is a string literal.
|
|
@@ -116,11 +128,11 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|
|
116
128
|
def __post_init__(self):
|
|
117
129
|
"""Post-initialize the var."""
|
|
118
130
|
# Decode any inline Var markup and apply it to the instance
|
|
119
|
-
_var_data,
|
|
131
|
+
_var_data, _js_expr = _decode_var_immutable(self._js_expr)
|
|
120
132
|
|
|
121
|
-
if _var_data or
|
|
133
|
+
if _var_data or _js_expr != self._js_expr:
|
|
122
134
|
self.__init__(
|
|
123
|
-
|
|
135
|
+
_js_expr=_js_expr,
|
|
124
136
|
_var_type=self._var_type,
|
|
125
137
|
_var_data=VarData.merge(self._var_data, _var_data),
|
|
126
138
|
)
|
|
@@ -131,7 +143,7 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|
|
131
143
|
Returns:
|
|
132
144
|
The hash of the var.
|
|
133
145
|
"""
|
|
134
|
-
return hash((self.
|
|
146
|
+
return hash((self._js_expr, self._var_type, self._var_data))
|
|
135
147
|
|
|
136
148
|
def _get_all_var_data(self) -> VarData | None:
|
|
137
149
|
"""Get all VarData associated with the Var.
|
|
@@ -141,7 +153,7 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|
|
141
153
|
"""
|
|
142
154
|
return self._var_data
|
|
143
155
|
|
|
144
|
-
def equals(self, other:
|
|
156
|
+
def equals(self, other: Var) -> bool:
|
|
145
157
|
"""Check if two vars are equal.
|
|
146
158
|
|
|
147
159
|
Args:
|
|
@@ -151,7 +163,7 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|
|
151
163
|
Whether the vars are equal.
|
|
152
164
|
"""
|
|
153
165
|
return (
|
|
154
|
-
self.
|
|
166
|
+
self._js_expr == other._js_expr
|
|
155
167
|
and self._var_type == other._var_type
|
|
156
168
|
and self._get_all_var_data() == other._get_all_var_data()
|
|
157
169
|
)
|
|
@@ -164,24 +176,20 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|
|
164
176
|
**kwargs: Var fields to update.
|
|
165
177
|
|
|
166
178
|
Returns:
|
|
167
|
-
A new
|
|
179
|
+
A new Var with the updated fields overwriting the corresponding fields in this Var.
|
|
168
180
|
|
|
169
181
|
Raises:
|
|
170
182
|
TypeError: If _var_is_local, _var_is_string, or _var_full_name_needs_state_prefix is not None.
|
|
171
183
|
"""
|
|
172
184
|
if kwargs.get("_var_is_local", False) is not False:
|
|
173
|
-
raise TypeError(
|
|
174
|
-
"The _var_is_local argument is not supported for ImmutableVar."
|
|
175
|
-
)
|
|
185
|
+
raise TypeError("The _var_is_local argument is not supported for Var.")
|
|
176
186
|
|
|
177
187
|
if kwargs.get("_var_is_string", False) is not False:
|
|
178
|
-
raise TypeError(
|
|
179
|
-
"The _var_is_string argument is not supported for ImmutableVar."
|
|
180
|
-
)
|
|
188
|
+
raise TypeError("The _var_is_string argument is not supported for Var.")
|
|
181
189
|
|
|
182
190
|
if kwargs.get("_var_full_name_needs_state_prefix", False) is not False:
|
|
183
191
|
raise TypeError(
|
|
184
|
-
"The _var_full_name_needs_state_prefix argument is not supported for
|
|
192
|
+
"The _var_full_name_needs_state_prefix argument is not supported for Var."
|
|
185
193
|
)
|
|
186
194
|
|
|
187
195
|
return dataclasses.replace(
|
|
@@ -199,7 +207,7 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|
|
199
207
|
_var_is_local: bool | None = None,
|
|
200
208
|
_var_is_string: bool | None = None,
|
|
201
209
|
_var_data: VarData | None = None,
|
|
202
|
-
) ->
|
|
210
|
+
) -> Var:
|
|
203
211
|
"""Create a var from a value.
|
|
204
212
|
|
|
205
213
|
Args:
|
|
@@ -210,80 +218,57 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|
|
210
218
|
|
|
211
219
|
Returns:
|
|
212
220
|
The var.
|
|
213
|
-
|
|
214
|
-
Raises:
|
|
215
|
-
VarTypeError: If the value is JSON-unserializable.
|
|
216
|
-
TypeError: If _var_is_local or _var_is_string is not None.
|
|
217
221
|
"""
|
|
218
222
|
if _var_is_local is not None:
|
|
219
|
-
|
|
220
|
-
"
|
|
223
|
+
console.deprecate(
|
|
224
|
+
feature_name="_var_is_local",
|
|
225
|
+
reason="The _var_is_local argument is not supported for Var."
|
|
226
|
+
"If you want to create a Var from a raw Javascript expression, use the constructor directly",
|
|
227
|
+
deprecation_version="0.6.0",
|
|
228
|
+
removal_version="0.7.0",
|
|
221
229
|
)
|
|
222
|
-
|
|
223
230
|
if _var_is_string is not None:
|
|
224
|
-
|
|
225
|
-
"
|
|
231
|
+
console.deprecate(
|
|
232
|
+
feature_name="_var_is_string",
|
|
233
|
+
reason="The _var_is_string argument is not supported for Var."
|
|
234
|
+
"If you want to create a Var from a raw Javascript expression, use the constructor directly",
|
|
235
|
+
deprecation_version="0.6.0",
|
|
236
|
+
removal_version="0.7.0",
|
|
226
237
|
)
|
|
227
238
|
|
|
228
|
-
from reflex.utils import format
|
|
229
|
-
|
|
230
|
-
# Check for none values.
|
|
231
|
-
if value is None:
|
|
232
|
-
return None
|
|
233
|
-
|
|
234
239
|
# If the value is already a var, do nothing.
|
|
235
|
-
if isinstance(value,
|
|
240
|
+
if isinstance(value, Var):
|
|
236
241
|
return value
|
|
237
242
|
|
|
238
243
|
# Try to pull the imports and hooks from contained values.
|
|
239
244
|
if not isinstance(value, str):
|
|
240
|
-
|
|
245
|
+
return LiteralVar.create(value)
|
|
241
246
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
else:
|
|
247
|
-
name, _serialized_type = serializers.serialize(value, get_type=True)
|
|
248
|
-
if name is None:
|
|
249
|
-
raise VarTypeError(
|
|
250
|
-
f"No JSON serializer found for var {value} of type {type_}."
|
|
247
|
+
if _var_is_string is False or _var_is_local is True:
|
|
248
|
+
return cls(
|
|
249
|
+
_js_expr=value,
|
|
250
|
+
_var_data=_var_data,
|
|
251
251
|
)
|
|
252
|
-
name = name if isinstance(name, str) else format.json_dumps(name)
|
|
253
252
|
|
|
254
|
-
return
|
|
255
|
-
_var_name=name,
|
|
256
|
-
_var_type=type_,
|
|
257
|
-
_var_data=_var_data,
|
|
258
|
-
)
|
|
253
|
+
return LiteralVar.create(value, _var_data=_var_data)
|
|
259
254
|
|
|
260
255
|
@classmethod
|
|
256
|
+
@deprecated("Use `.create()` instead.")
|
|
261
257
|
def create_safe(
|
|
262
258
|
cls,
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
) -> ImmutableVar:
|
|
268
|
-
"""Create a var from a value, asserting that it is not None.
|
|
259
|
+
*args: Any,
|
|
260
|
+
**kwargs: Any,
|
|
261
|
+
) -> Var:
|
|
262
|
+
"""Create a var from a value.
|
|
269
263
|
|
|
270
264
|
Args:
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
_var_is_string: Whether the var is a string literal. Deprecated.
|
|
274
|
-
_var_data: Additional hooks and imports associated with the Var.
|
|
265
|
+
*args: The arguments to create the var from.
|
|
266
|
+
**kwargs: The keyword arguments to create the var from.
|
|
275
267
|
|
|
276
268
|
Returns:
|
|
277
269
|
The var.
|
|
278
270
|
"""
|
|
279
|
-
|
|
280
|
-
value,
|
|
281
|
-
_var_is_local=_var_is_local,
|
|
282
|
-
_var_is_string=_var_is_string,
|
|
283
|
-
_var_data=_var_data,
|
|
284
|
-
)
|
|
285
|
-
assert var is not None
|
|
286
|
-
return var
|
|
271
|
+
return cls.create(*args, **kwargs)
|
|
287
272
|
|
|
288
273
|
def __format__(self, format_spec: str) -> str:
|
|
289
274
|
"""Format the var into a Javascript equivalent to an f-string.
|
|
@@ -299,7 +284,7 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|
|
299
284
|
_global_vars[hashed_var] = self
|
|
300
285
|
|
|
301
286
|
# Encode the _var_data into the formatted output for tracking purposes.
|
|
302
|
-
return f"{constants.REFLEX_VAR_OPENING_TAG}{hashed_var}{constants.REFLEX_VAR_CLOSING_TAG}{self.
|
|
287
|
+
return f"{constants.REFLEX_VAR_OPENING_TAG}{hashed_var}{constants.REFLEX_VAR_CLOSING_TAG}{self._js_expr}"
|
|
303
288
|
|
|
304
289
|
@overload
|
|
305
290
|
def to(self, output: Type[StringVar]) -> ToStringOperation: ...
|
|
@@ -343,7 +328,7 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|
|
343
328
|
self,
|
|
344
329
|
output: Type[OUTPUT] | types.GenericType,
|
|
345
330
|
var_type: types.GenericType | None = None,
|
|
346
|
-
) ->
|
|
331
|
+
) -> Var:
|
|
347
332
|
"""Convert the var to a different type.
|
|
348
333
|
|
|
349
334
|
Args:
|
|
@@ -387,6 +372,12 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|
|
387
372
|
return self.to(BooleanVar, output)
|
|
388
373
|
if fixed_output_type is None:
|
|
389
374
|
return ToNoneOperation.create(self)
|
|
375
|
+
if issubclass(fixed_output_type, Base):
|
|
376
|
+
return self.to(ObjectVar, output)
|
|
377
|
+
if dataclasses.is_dataclass(fixed_output_type) and not issubclass(
|
|
378
|
+
fixed_output_type, Var
|
|
379
|
+
):
|
|
380
|
+
return self.to(ObjectVar, output)
|
|
390
381
|
|
|
391
382
|
if issubclass(output, BooleanVar):
|
|
392
383
|
return ToBooleanVarOperation.create(self)
|
|
@@ -421,6 +412,9 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|
|
421
412
|
if issubclass(output, (ObjectVar, Base)):
|
|
422
413
|
return ToObjectOperation.create(self, var_type or dict)
|
|
423
414
|
|
|
415
|
+
if dataclasses.is_dataclass(output):
|
|
416
|
+
return ToObjectOperation.create(self, var_type or dict)
|
|
417
|
+
|
|
424
418
|
if issubclass(output, FunctionVar):
|
|
425
419
|
# if fixed_type is not None and not issubclass(fixed_type, Callable):
|
|
426
420
|
# raise TypeError(
|
|
@@ -447,11 +441,11 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|
|
447
441
|
|
|
448
442
|
return self
|
|
449
443
|
|
|
450
|
-
def guess_type(self) ->
|
|
444
|
+
def guess_type(self) -> Var:
|
|
451
445
|
"""Guesses the type of the variable based on its `_var_type` attribute.
|
|
452
446
|
|
|
453
447
|
Returns:
|
|
454
|
-
|
|
448
|
+
Var: The guessed type of the variable.
|
|
455
449
|
|
|
456
450
|
Raises:
|
|
457
451
|
TypeError: If the type is not supported for guessing.
|
|
@@ -479,7 +473,11 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|
|
479
473
|
):
|
|
480
474
|
return self.to(NumberVar, self._var_type)
|
|
481
475
|
|
|
482
|
-
if all(
|
|
476
|
+
if all(
|
|
477
|
+
inspect.isclass(t)
|
|
478
|
+
and (issubclass(t, Base) or dataclasses.is_dataclass(t))
|
|
479
|
+
for t in inner_types
|
|
480
|
+
):
|
|
483
481
|
return self.to(ObjectVar, self._var_type)
|
|
484
482
|
|
|
485
483
|
return self
|
|
@@ -499,6 +497,8 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|
|
499
497
|
return self.to(StringVar, self._var_type)
|
|
500
498
|
if issubclass(fixed_type, Base):
|
|
501
499
|
return self.to(ObjectVar, self._var_type)
|
|
500
|
+
if dataclasses.is_dataclass(fixed_type):
|
|
501
|
+
return self.to(ObjectVar, self._var_type)
|
|
502
502
|
return self
|
|
503
503
|
|
|
504
504
|
def get_default_value(self) -> Any:
|
|
@@ -553,7 +553,7 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|
|
553
553
|
Returns:
|
|
554
554
|
The name of the setter function.
|
|
555
555
|
"""
|
|
556
|
-
var_name_parts = self.
|
|
556
|
+
var_name_parts = self._js_expr.split(".")
|
|
557
557
|
setter = constants.SETTER_PREFIX + var_name_parts[-1]
|
|
558
558
|
var_data = self._get_all_var_data()
|
|
559
559
|
if var_data is None:
|
|
@@ -568,7 +568,7 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|
|
568
568
|
Returns:
|
|
569
569
|
A function that that creates a setter for the var.
|
|
570
570
|
"""
|
|
571
|
-
actual_name = self.
|
|
571
|
+
actual_name = self._js_expr.split(".")[-1]
|
|
572
572
|
|
|
573
573
|
def setter(state: BaseState, value: Any):
|
|
574
574
|
"""Get the setter for the var.
|
|
@@ -583,7 +583,7 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|
|
583
583
|
setattr(state, actual_name, value)
|
|
584
584
|
except ValueError:
|
|
585
585
|
console.debug(
|
|
586
|
-
f"{type(state).__name__}.{self.
|
|
586
|
+
f"{type(state).__name__}.{self._js_expr}: Failed conversion of {value} to '{self._var_type.__name__}'. Value not set.",
|
|
587
587
|
)
|
|
588
588
|
else:
|
|
589
589
|
setattr(state, actual_name, value)
|
|
@@ -613,11 +613,11 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|
|
613
613
|
_var_data=VarData.merge(VarData.from_state(state), self._var_data),
|
|
614
614
|
).guess_type()
|
|
615
615
|
|
|
616
|
-
def __eq__(self, other:
|
|
616
|
+
def __eq__(self, other: Var | Any) -> BooleanVar:
|
|
617
617
|
"""Check if the current variable is equal to the given variable.
|
|
618
618
|
|
|
619
619
|
Args:
|
|
620
|
-
other (
|
|
620
|
+
other (Var | Any): The variable to compare with.
|
|
621
621
|
|
|
622
622
|
Returns:
|
|
623
623
|
BooleanVar: A BooleanVar object representing the result of the equality check.
|
|
@@ -626,11 +626,11 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|
|
626
626
|
|
|
627
627
|
return equal_operation(self, other)
|
|
628
628
|
|
|
629
|
-
def __ne__(self, other:
|
|
629
|
+
def __ne__(self, other: Var | Any) -> BooleanVar:
|
|
630
630
|
"""Check if the current object is not equal to the given object.
|
|
631
631
|
|
|
632
632
|
Parameters:
|
|
633
|
-
other (
|
|
633
|
+
other (Var | Any): The object to compare with.
|
|
634
634
|
|
|
635
635
|
Returns:
|
|
636
636
|
BooleanVar: A BooleanVar object representing the result of the comparison.
|
|
@@ -649,7 +649,7 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|
|
649
649
|
|
|
650
650
|
return boolify(self)
|
|
651
651
|
|
|
652
|
-
def __and__(self, other:
|
|
652
|
+
def __and__(self, other: Var | Any) -> Var:
|
|
653
653
|
"""Perform a logical AND operation on the current instance and another variable.
|
|
654
654
|
|
|
655
655
|
Args:
|
|
@@ -660,7 +660,7 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|
|
660
660
|
"""
|
|
661
661
|
return and_operation(self, other)
|
|
662
662
|
|
|
663
|
-
def __rand__(self, other:
|
|
663
|
+
def __rand__(self, other: Var | Any) -> Var:
|
|
664
664
|
"""Perform a logical AND operation on the current instance and another variable.
|
|
665
665
|
|
|
666
666
|
Args:
|
|
@@ -671,7 +671,7 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|
|
671
671
|
"""
|
|
672
672
|
return and_operation(other, self)
|
|
673
673
|
|
|
674
|
-
def __or__(self, other:
|
|
674
|
+
def __or__(self, other: Var | Any) -> Var:
|
|
675
675
|
"""Perform a logical OR operation on the current instance and another variable.
|
|
676
676
|
|
|
677
677
|
Args:
|
|
@@ -682,7 +682,7 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|
|
682
682
|
"""
|
|
683
683
|
return or_operation(self, other)
|
|
684
684
|
|
|
685
|
-
def __ror__(self, other:
|
|
685
|
+
def __ror__(self, other: Var | Any) -> Var:
|
|
686
686
|
"""Perform a logical OR operation on the current instance and another variable.
|
|
687
687
|
|
|
688
688
|
Args:
|
|
@@ -712,7 +712,7 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|
|
712
712
|
|
|
713
713
|
return JSON_STRINGIFY.call(self).to(StringVar)
|
|
714
714
|
|
|
715
|
-
def as_ref(self) ->
|
|
715
|
+
def as_ref(self) -> Var:
|
|
716
716
|
"""Get a reference to the var.
|
|
717
717
|
|
|
718
718
|
Returns:
|
|
@@ -720,14 +720,14 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|
|
720
720
|
"""
|
|
721
721
|
from .object import ObjectVar
|
|
722
722
|
|
|
723
|
-
refs =
|
|
724
|
-
|
|
723
|
+
refs = Var(
|
|
724
|
+
_js_expr="refs",
|
|
725
725
|
_var_data=VarData(
|
|
726
726
|
imports={
|
|
727
727
|
f"/{constants.Dirs.STATE_PATH}": [imports.ImportVar(tag="refs")]
|
|
728
728
|
}
|
|
729
729
|
),
|
|
730
|
-
).to(ObjectVar)
|
|
730
|
+
).to(ObjectVar, Dict[str, str])
|
|
731
731
|
return refs[LiteralVar.create(str(self))]
|
|
732
732
|
|
|
733
733
|
@deprecated("Use `.js_type()` instead.")
|
|
@@ -813,7 +813,8 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|
|
813
813
|
TypeError: If the var type is Any.
|
|
814
814
|
"""
|
|
815
815
|
if name.startswith("_"):
|
|
816
|
-
return
|
|
816
|
+
return self.__getattribute__(name)
|
|
817
|
+
|
|
817
818
|
if self._var_type is Any:
|
|
818
819
|
raise TypeError(
|
|
819
820
|
f"You must provide an annotation for the state var `{str(self)}`. Annotation cannot be `{self._var_type}`."
|
|
@@ -857,26 +858,85 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
|
|
|
857
858
|
var_data = self._get_all_var_data()
|
|
858
859
|
return var_data.state if var_data else ""
|
|
859
860
|
|
|
861
|
+
@overload
|
|
862
|
+
@classmethod
|
|
863
|
+
def range(cls, stop: int | NumberVar, /) -> ArrayVar[List[int]]: ...
|
|
864
|
+
|
|
865
|
+
@overload
|
|
866
|
+
@classmethod
|
|
867
|
+
def range(
|
|
868
|
+
cls,
|
|
869
|
+
start: int | NumberVar,
|
|
870
|
+
end: int | NumberVar,
|
|
871
|
+
step: int | NumberVar = 1,
|
|
872
|
+
/,
|
|
873
|
+
) -> ArrayVar[List[int]]: ...
|
|
860
874
|
|
|
861
|
-
|
|
875
|
+
@classmethod
|
|
876
|
+
def range(
|
|
877
|
+
cls,
|
|
878
|
+
first_endpoint: int | NumberVar,
|
|
879
|
+
second_endpoint: int | NumberVar | None = None,
|
|
880
|
+
step: int | NumberVar | None = None,
|
|
881
|
+
) -> ArrayVar[List[int]]:
|
|
882
|
+
"""Create a range of numbers.
|
|
862
883
|
|
|
884
|
+
Args:
|
|
885
|
+
first_endpoint: The end of the range if second_endpoint is not provided, otherwise the start of the range.
|
|
886
|
+
second_endpoint: The end of the range.
|
|
887
|
+
step: The step of the range.
|
|
863
888
|
|
|
864
|
-
|
|
865
|
-
|
|
889
|
+
Returns:
|
|
890
|
+
The range of numbers.
|
|
891
|
+
"""
|
|
892
|
+
from .sequence import ArrayVar
|
|
866
893
|
|
|
867
|
-
|
|
868
|
-
value: The value to encode the state name into.
|
|
894
|
+
return ArrayVar.range(first_endpoint, second_endpoint, step)
|
|
869
895
|
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
896
|
+
def __bool__(self) -> bool:
|
|
897
|
+
"""Raise exception if using Var in a boolean context.
|
|
898
|
+
|
|
899
|
+
Raises:
|
|
900
|
+
VarTypeError: when attempting to bool-ify the Var.
|
|
901
|
+
"""
|
|
902
|
+
raise VarTypeError(
|
|
903
|
+
f"Cannot convert Var {str(self)!r} to bool for use with `if`, `and`, `or`, and `not`. "
|
|
904
|
+
"Instead use `rx.cond` and bitwise operators `&` (and), `|` (or), `~` (invert)."
|
|
905
|
+
)
|
|
906
|
+
|
|
907
|
+
def __iter__(self) -> Any:
|
|
908
|
+
"""Raise exception if using Var in an iterable context.
|
|
909
|
+
|
|
910
|
+
Raises:
|
|
911
|
+
VarTypeError: when attempting to iterate over the Var.
|
|
912
|
+
"""
|
|
913
|
+
raise VarTypeError(
|
|
914
|
+
f"Cannot iterate over Var {str(self)!r}. Instead use `rx.foreach`."
|
|
915
|
+
)
|
|
916
|
+
|
|
917
|
+
def __contains__(self, _: Any) -> Var:
|
|
918
|
+
"""Override the 'in' operator to alert the user that it is not supported.
|
|
919
|
+
|
|
920
|
+
Raises:
|
|
921
|
+
VarTypeError: the operation is not supported
|
|
922
|
+
"""
|
|
923
|
+
raise VarTypeError(
|
|
924
|
+
"'in' operator not supported for Var types, use Var.contains() instead."
|
|
925
|
+
)
|
|
926
|
+
|
|
927
|
+
def json(self) -> str:
|
|
928
|
+
"""Serialize the var to a JSON string.
|
|
929
|
+
|
|
930
|
+
Raises:
|
|
931
|
+
NotImplementedError: If the method is not implemented.
|
|
932
|
+
"""
|
|
933
|
+
raise NotImplementedError("Var subclasses must implement the json method.")
|
|
874
934
|
|
|
875
935
|
|
|
876
|
-
|
|
936
|
+
OUTPUT = TypeVar("OUTPUT", bound=Var)
|
|
877
937
|
|
|
878
938
|
|
|
879
|
-
class LiteralVar(
|
|
939
|
+
class LiteralVar(Var):
|
|
880
940
|
"""Base class for immutable literal vars."""
|
|
881
941
|
|
|
882
942
|
@classmethod
|
|
@@ -884,7 +944,7 @@ class LiteralVar(ImmutableVar):
|
|
|
884
944
|
cls,
|
|
885
945
|
value: Any,
|
|
886
946
|
_var_data: VarData | None = None,
|
|
887
|
-
) ->
|
|
947
|
+
) -> Var:
|
|
888
948
|
"""Create a var from a value.
|
|
889
949
|
|
|
890
950
|
Args:
|
|
@@ -901,7 +961,7 @@ class LiteralVar(ImmutableVar):
|
|
|
901
961
|
from .object import LiteralObjectVar
|
|
902
962
|
from .sequence import LiteralArrayVar, LiteralStringVar
|
|
903
963
|
|
|
904
|
-
if isinstance(value,
|
|
964
|
+
if isinstance(value, Var):
|
|
905
965
|
if _var_data is None:
|
|
906
966
|
return value
|
|
907
967
|
return value._replace(merge_var_data=_var_data)
|
|
@@ -924,7 +984,7 @@ class LiteralVar(ImmutableVar):
|
|
|
924
984
|
if value is None:
|
|
925
985
|
return LiteralNoneVar.create(_var_data=_var_data)
|
|
926
986
|
|
|
927
|
-
from reflex.event import EventChain, EventSpec
|
|
987
|
+
from reflex.event import EventChain, EventHandler, EventSpec
|
|
928
988
|
from reflex.utils.format import get_event_handler_parts
|
|
929
989
|
|
|
930
990
|
from .function import ArgsFunctionOperation, FunctionStringVar
|
|
@@ -948,14 +1008,12 @@ class LiteralVar(ImmutableVar):
|
|
|
948
1008
|
sig = inspect.signature(value.args_spec) # type: ignore
|
|
949
1009
|
if sig.parameters:
|
|
950
1010
|
arg_def = tuple((f"_{p}" for p in sig.parameters))
|
|
951
|
-
arg_def_expr = LiteralVar.create(
|
|
952
|
-
[ImmutableVar.create_safe(arg) for arg in arg_def]
|
|
953
|
-
)
|
|
1011
|
+
arg_def_expr = LiteralVar.create([Var(_js_expr=arg) for arg in arg_def])
|
|
954
1012
|
else:
|
|
955
1013
|
# add a default argument for addEvents if none were specified in value.args_spec
|
|
956
1014
|
# used to trigger the preventDefault() on the event.
|
|
957
1015
|
arg_def = ("...args",)
|
|
958
|
-
arg_def_expr =
|
|
1016
|
+
arg_def_expr = Var(_js_expr="args")
|
|
959
1017
|
|
|
960
1018
|
return ArgsFunctionOperation.create(
|
|
961
1019
|
arg_def,
|
|
@@ -968,9 +1026,20 @@ class LiteralVar(ImmutableVar):
|
|
|
968
1026
|
),
|
|
969
1027
|
)
|
|
970
1028
|
|
|
1029
|
+
if isinstance(value, EventHandler):
|
|
1030
|
+
return Var(_js_expr=".".join(filter(None, get_event_handler_parts(value))))
|
|
1031
|
+
|
|
971
1032
|
if isinstance(value, Base):
|
|
1033
|
+
# get the fields of the pydantic class
|
|
1034
|
+
fields = value.__fields__.keys()
|
|
1035
|
+
one_level_dict = {field: getattr(value, field) for field in fields}
|
|
1036
|
+
|
|
972
1037
|
return LiteralObjectVar.create(
|
|
973
|
-
{
|
|
1038
|
+
{
|
|
1039
|
+
field: value
|
|
1040
|
+
for field, value in one_level_dict.items()
|
|
1041
|
+
if not callable(value)
|
|
1042
|
+
},
|
|
974
1043
|
_var_type=type(value),
|
|
975
1044
|
_var_data=_var_data,
|
|
976
1045
|
)
|
|
@@ -983,8 +1052,22 @@ class LiteralVar(ImmutableVar):
|
|
|
983
1052
|
_var_type=type(value),
|
|
984
1053
|
_var_data=_var_data,
|
|
985
1054
|
)
|
|
1055
|
+
if isinstance(serialized_value, str):
|
|
1056
|
+
return LiteralStringVar.create(
|
|
1057
|
+
serialized_value, _var_type=type(value), _var_data=_var_data
|
|
1058
|
+
)
|
|
986
1059
|
return LiteralVar.create(serialized_value, _var_data=_var_data)
|
|
987
1060
|
|
|
1061
|
+
if dataclasses.is_dataclass(value) and not isinstance(value, type):
|
|
1062
|
+
return LiteralObjectVar.create(
|
|
1063
|
+
{
|
|
1064
|
+
k: (None if callable(v) else v)
|
|
1065
|
+
for k, v in dataclasses.asdict(value).items()
|
|
1066
|
+
},
|
|
1067
|
+
_var_type=type(value),
|
|
1068
|
+
_var_data=_var_data,
|
|
1069
|
+
)
|
|
1070
|
+
|
|
988
1071
|
raise TypeError(
|
|
989
1072
|
f"Unsupported type {type(value)} for LiteralVar. Tried to create a LiteralVar from {value}."
|
|
990
1073
|
)
|
|
@@ -1003,6 +1086,19 @@ class LiteralVar(ImmutableVar):
|
|
|
1003
1086
|
)
|
|
1004
1087
|
|
|
1005
1088
|
|
|
1089
|
+
@serializers.serializer
|
|
1090
|
+
def serialize_literal(value: LiteralVar):
|
|
1091
|
+
"""Serialize a Literal type.
|
|
1092
|
+
|
|
1093
|
+
Args:
|
|
1094
|
+
value: The Literal to serialize.
|
|
1095
|
+
|
|
1096
|
+
Returns:
|
|
1097
|
+
The serialized Literal.
|
|
1098
|
+
"""
|
|
1099
|
+
return serializers.serialize(value._var_value)
|
|
1100
|
+
|
|
1101
|
+
|
|
1006
1102
|
P = ParamSpec("P")
|
|
1007
1103
|
T = TypeVar("T")
|
|
1008
1104
|
|
|
@@ -1011,7 +1107,7 @@ T = TypeVar("T")
|
|
|
1011
1107
|
@overload
|
|
1012
1108
|
def var_operation(
|
|
1013
1109
|
func: Callable[P, CustomVarOperationReturn[NoReturn]],
|
|
1014
|
-
) -> Callable[P,
|
|
1110
|
+
) -> Callable[P, Var]: ...
|
|
1015
1111
|
|
|
1016
1112
|
|
|
1017
1113
|
@overload
|
|
@@ -1055,7 +1151,7 @@ def var_operation(
|
|
|
1055
1151
|
|
|
1056
1152
|
def var_operation(
|
|
1057
1153
|
func: Callable[P, CustomVarOperationReturn[T]],
|
|
1058
|
-
) -> Callable[P,
|
|
1154
|
+
) -> Callable[P, Var[T]]:
|
|
1059
1155
|
"""Decorator for creating a var operation.
|
|
1060
1156
|
|
|
1061
1157
|
Example:
|
|
@@ -1073,18 +1169,14 @@ def var_operation(
|
|
|
1073
1169
|
"""
|
|
1074
1170
|
|
|
1075
1171
|
@functools.wraps(func)
|
|
1076
|
-
def wrapper(*args: P.args, **kwargs: P.kwargs) ->
|
|
1172
|
+
def wrapper(*args: P.args, **kwargs: P.kwargs) -> Var[T]:
|
|
1077
1173
|
func_args = list(inspect.signature(func).parameters)
|
|
1078
1174
|
args_vars = {
|
|
1079
|
-
func_args[i]: (
|
|
1080
|
-
LiteralVar.create(arg) if not isinstance(arg, ImmutableVar) else arg
|
|
1081
|
-
)
|
|
1175
|
+
func_args[i]: (LiteralVar.create(arg) if not isinstance(arg, Var) else arg)
|
|
1082
1176
|
for i, arg in enumerate(args)
|
|
1083
1177
|
}
|
|
1084
1178
|
kwargs_vars = {
|
|
1085
|
-
key: LiteralVar.create(value)
|
|
1086
|
-
if not isinstance(value, ImmutableVar)
|
|
1087
|
-
else value
|
|
1179
|
+
key: LiteralVar.create(value) if not isinstance(value, Var) else value
|
|
1088
1180
|
for key, value in kwargs.items()
|
|
1089
1181
|
}
|
|
1090
1182
|
|
|
@@ -1133,7 +1225,7 @@ def figure_out_type(value: Any) -> types.GenericType:
|
|
|
1133
1225
|
unionize(*(figure_out_type(k) for k in value)),
|
|
1134
1226
|
unionize(*(figure_out_type(v) for v in value.values())),
|
|
1135
1227
|
]
|
|
1136
|
-
if isinstance(value,
|
|
1228
|
+
if isinstance(value, Var):
|
|
1137
1229
|
return value._var_type
|
|
1138
1230
|
return type(value)
|
|
1139
1231
|
|
|
@@ -1156,7 +1248,7 @@ class CachedVarOperation:
|
|
|
1156
1248
|
|
|
1157
1249
|
def __post_init__(self):
|
|
1158
1250
|
"""Post-initialize the CachedVarOperation."""
|
|
1159
|
-
object.__delattr__(self, "
|
|
1251
|
+
object.__delattr__(self, "_js_expr")
|
|
1160
1252
|
|
|
1161
1253
|
def __getattr__(self, name: str) -> Any:
|
|
1162
1254
|
"""Get an attribute of the var.
|
|
@@ -1167,7 +1259,7 @@ class CachedVarOperation:
|
|
|
1167
1259
|
Returns:
|
|
1168
1260
|
The attribute.
|
|
1169
1261
|
"""
|
|
1170
|
-
if name == "
|
|
1262
|
+
if name == "_js_expr":
|
|
1171
1263
|
return self._cached_var_name
|
|
1172
1264
|
|
|
1173
1265
|
parent_classes = inspect.getmro(self.__class__)
|
|
@@ -1194,9 +1286,7 @@ class CachedVarOperation:
|
|
|
1194
1286
|
return VarData.merge(
|
|
1195
1287
|
*map(
|
|
1196
1288
|
lambda value: (
|
|
1197
|
-
value._get_all_var_data()
|
|
1198
|
-
if isinstance(value, ImmutableVar)
|
|
1199
|
-
else None
|
|
1289
|
+
value._get_all_var_data() if isinstance(value, Var) else None
|
|
1200
1290
|
),
|
|
1201
1291
|
map(
|
|
1202
1292
|
lambda field: getattr(self, field.name),
|
|
@@ -1218,13 +1308,13 @@ class CachedVarOperation:
|
|
|
1218
1308
|
*[
|
|
1219
1309
|
getattr(self, field.name)
|
|
1220
1310
|
for field in dataclasses.fields(self) # type: ignore
|
|
1221
|
-
if field.name not in ["
|
|
1311
|
+
if field.name not in ["_js_expr", "_var_data", "_var_type"]
|
|
1222
1312
|
],
|
|
1223
1313
|
)
|
|
1224
1314
|
)
|
|
1225
1315
|
|
|
1226
1316
|
|
|
1227
|
-
def and_operation(a:
|
|
1317
|
+
def and_operation(a: Var | Any, b: Var | Any) -> Var:
|
|
1228
1318
|
"""Perform a logical AND operation on two variables.
|
|
1229
1319
|
|
|
1230
1320
|
Args:
|
|
@@ -1238,7 +1328,7 @@ def and_operation(a: ImmutableVar | Any, b: ImmutableVar | Any) -> ImmutableVar:
|
|
|
1238
1328
|
|
|
1239
1329
|
|
|
1240
1330
|
@var_operation
|
|
1241
|
-
def _and_operation(a:
|
|
1331
|
+
def _and_operation(a: Var, b: Var):
|
|
1242
1332
|
"""Perform a logical AND operation on two variables.
|
|
1243
1333
|
|
|
1244
1334
|
Args:
|
|
@@ -1254,7 +1344,7 @@ def _and_operation(a: ImmutableVar, b: ImmutableVar):
|
|
|
1254
1344
|
)
|
|
1255
1345
|
|
|
1256
1346
|
|
|
1257
|
-
def or_operation(a:
|
|
1347
|
+
def or_operation(a: Var | Any, b: Var | Any) -> Var:
|
|
1258
1348
|
"""Perform a logical OR operation on two variables.
|
|
1259
1349
|
|
|
1260
1350
|
Args:
|
|
@@ -1268,7 +1358,7 @@ def or_operation(a: ImmutableVar | Any, b: ImmutableVar | Any) -> ImmutableVar:
|
|
|
1268
1358
|
|
|
1269
1359
|
|
|
1270
1360
|
@var_operation
|
|
1271
|
-
def _or_operation(a:
|
|
1361
|
+
def _or_operation(a: Var, b: Var):
|
|
1272
1362
|
"""Perform a logical OR operation on two variables.
|
|
1273
1363
|
|
|
1274
1364
|
Args:
|
|
@@ -1289,36 +1379,36 @@ def _or_operation(a: ImmutableVar, b: ImmutableVar):
|
|
|
1289
1379
|
frozen=True,
|
|
1290
1380
|
**{"slots": True} if sys.version_info >= (3, 10) else {},
|
|
1291
1381
|
)
|
|
1292
|
-
class
|
|
1382
|
+
class CallableVar(Var):
|
|
1293
1383
|
"""Decorate a Var-returning function to act as both a Var and a function.
|
|
1294
1384
|
|
|
1295
1385
|
This is used as a compatibility shim for replacing Var objects in the
|
|
1296
1386
|
API with functions that return a family of Var.
|
|
1297
1387
|
"""
|
|
1298
1388
|
|
|
1299
|
-
fn: Callable[...,
|
|
1300
|
-
default_factory=lambda: lambda:
|
|
1389
|
+
fn: Callable[..., Var] = dataclasses.field(
|
|
1390
|
+
default_factory=lambda: lambda: Var(_js_expr="undefined")
|
|
1301
1391
|
)
|
|
1302
|
-
original_var:
|
|
1303
|
-
default_factory=lambda:
|
|
1392
|
+
original_var: Var = dataclasses.field(
|
|
1393
|
+
default_factory=lambda: Var(_js_expr="undefined")
|
|
1304
1394
|
)
|
|
1305
1395
|
|
|
1306
|
-
def __init__(self, fn: Callable[...,
|
|
1396
|
+
def __init__(self, fn: Callable[..., Var]):
|
|
1307
1397
|
"""Initialize a CallableVar.
|
|
1308
1398
|
|
|
1309
1399
|
Args:
|
|
1310
1400
|
fn: The function to decorate (must return Var)
|
|
1311
1401
|
"""
|
|
1312
1402
|
original_var = fn()
|
|
1313
|
-
super(
|
|
1314
|
-
|
|
1403
|
+
super(CallableVar, self).__init__(
|
|
1404
|
+
_js_expr=original_var._js_expr,
|
|
1315
1405
|
_var_type=original_var._var_type,
|
|
1316
1406
|
_var_data=VarData.merge(original_var._get_all_var_data()),
|
|
1317
1407
|
)
|
|
1318
1408
|
object.__setattr__(self, "fn", fn)
|
|
1319
1409
|
object.__setattr__(self, "original_var", original_var)
|
|
1320
1410
|
|
|
1321
|
-
def __call__(self, *args, **kwargs) ->
|
|
1411
|
+
def __call__(self, *args, **kwargs) -> Var:
|
|
1322
1412
|
"""Call the decorated function.
|
|
1323
1413
|
|
|
1324
1414
|
Args:
|
|
@@ -1347,13 +1437,13 @@ DICT_VAL = TypeVar("DICT_VAL")
|
|
|
1347
1437
|
LIST_INSIDE = TypeVar("LIST_INSIDE")
|
|
1348
1438
|
|
|
1349
1439
|
|
|
1350
|
-
class FakeComputedVarBaseClass(
|
|
1440
|
+
class FakeComputedVarBaseClass(property):
|
|
1351
1441
|
"""A fake base class for ComputedVar to avoid inheriting from property."""
|
|
1352
1442
|
|
|
1353
1443
|
__pydantic_run_validation__ = False
|
|
1354
1444
|
|
|
1355
1445
|
|
|
1356
|
-
def is_computed_var(obj: Any) -> TypeGuard[
|
|
1446
|
+
def is_computed_var(obj: Any) -> TypeGuard[ComputedVar]:
|
|
1357
1447
|
"""Check if the object is a ComputedVar.
|
|
1358
1448
|
|
|
1359
1449
|
Args:
|
|
@@ -1370,7 +1460,7 @@ def is_computed_var(obj: Any) -> TypeGuard[ImmutableComputedVar]:
|
|
|
1370
1460
|
frozen=True,
|
|
1371
1461
|
**{"slots": True} if sys.version_info >= (3, 10) else {},
|
|
1372
1462
|
)
|
|
1373
|
-
class
|
|
1463
|
+
class ComputedVar(Var[RETURN_TYPE]):
|
|
1374
1464
|
"""A field with computed getters."""
|
|
1375
1465
|
|
|
1376
1466
|
# Whether to track dependencies and cache computed values
|
|
@@ -1400,7 +1490,7 @@ class ImmutableComputedVar(ImmutableVar[RETURN_TYPE]):
|
|
|
1400
1490
|
fget: Callable[[BASE_STATE], RETURN_TYPE],
|
|
1401
1491
|
initial_value: RETURN_TYPE | types.Unset = types.Unset(),
|
|
1402
1492
|
cache: bool = False,
|
|
1403
|
-
deps: Optional[List[Union[str,
|
|
1493
|
+
deps: Optional[List[Union[str, Var]]] = None,
|
|
1404
1494
|
auto_deps: bool = True,
|
|
1405
1495
|
interval: Optional[Union[int, datetime.timedelta]] = None,
|
|
1406
1496
|
backend: bool | None = None,
|
|
@@ -1424,12 +1514,12 @@ class ImmutableComputedVar(ImmutableVar[RETURN_TYPE]):
|
|
|
1424
1514
|
hints = get_type_hints(fget)
|
|
1425
1515
|
hint = hints.get("return", Any)
|
|
1426
1516
|
|
|
1427
|
-
kwargs["
|
|
1517
|
+
kwargs["_js_expr"] = kwargs.pop("_js_expr", fget.__name__)
|
|
1428
1518
|
kwargs["_var_type"] = kwargs.pop("_var_type", hint)
|
|
1429
1519
|
|
|
1430
|
-
|
|
1520
|
+
Var.__init__(
|
|
1431
1521
|
self,
|
|
1432
|
-
|
|
1522
|
+
_js_expr=kwargs.pop("_js_expr"),
|
|
1433
1523
|
_var_type=kwargs.pop("_var_type"),
|
|
1434
1524
|
_var_data=kwargs.pop("_var_data", None),
|
|
1435
1525
|
)
|
|
@@ -1450,7 +1540,7 @@ class ImmutableComputedVar(ImmutableVar[RETURN_TYPE]):
|
|
|
1450
1540
|
deps = []
|
|
1451
1541
|
else:
|
|
1452
1542
|
for dep in deps:
|
|
1453
|
-
if isinstance(dep,
|
|
1543
|
+
if isinstance(dep, Var):
|
|
1454
1544
|
continue
|
|
1455
1545
|
if isinstance(dep, str) and dep != "":
|
|
1456
1546
|
continue
|
|
@@ -1460,7 +1550,7 @@ class ImmutableComputedVar(ImmutableVar[RETURN_TYPE]):
|
|
|
1460
1550
|
object.__setattr__(
|
|
1461
1551
|
self,
|
|
1462
1552
|
"_static_deps",
|
|
1463
|
-
{dep.
|
|
1553
|
+
{dep._js_expr if isinstance(dep, Var) else dep for dep in deps},
|
|
1464
1554
|
)
|
|
1465
1555
|
object.__setattr__(self, "_auto_deps", auto_deps)
|
|
1466
1556
|
|
|
@@ -1488,7 +1578,7 @@ class ImmutableComputedVar(ImmutableVar[RETURN_TYPE]):
|
|
|
1488
1578
|
auto_deps=kwargs.pop("auto_deps", self._auto_deps),
|
|
1489
1579
|
interval=kwargs.pop("interval", self._update_interval),
|
|
1490
1580
|
backend=kwargs.pop("backend", self._backend),
|
|
1491
|
-
|
|
1581
|
+
_js_expr=kwargs.pop("_js_expr", self._js_expr),
|
|
1492
1582
|
_var_type=kwargs.pop("_var_type", self._var_type),
|
|
1493
1583
|
_var_data=kwargs.pop(
|
|
1494
1584
|
"_var_data", VarData.merge(self._var_data, merge_var_data)
|
|
@@ -1508,7 +1598,7 @@ class ImmutableComputedVar(ImmutableVar[RETURN_TYPE]):
|
|
|
1508
1598
|
Returns:
|
|
1509
1599
|
An attribute name.
|
|
1510
1600
|
"""
|
|
1511
|
-
return f"__cached_{self.
|
|
1601
|
+
return f"__cached_{self._js_expr}"
|
|
1512
1602
|
|
|
1513
1603
|
@property
|
|
1514
1604
|
def _last_updated_attr(self) -> str:
|
|
@@ -1517,7 +1607,7 @@ class ImmutableComputedVar(ImmutableVar[RETURN_TYPE]):
|
|
|
1517
1607
|
Returns:
|
|
1518
1608
|
An attribute name.
|
|
1519
1609
|
"""
|
|
1520
|
-
return f"__last_updated_{self.
|
|
1610
|
+
return f"__last_updated_{self._js_expr}"
|
|
1521
1611
|
|
|
1522
1612
|
def needs_update(self, instance: BaseState) -> bool:
|
|
1523
1613
|
"""Check if the computed var needs to be updated.
|
|
@@ -1537,50 +1627,48 @@ class ImmutableComputedVar(ImmutableVar[RETURN_TYPE]):
|
|
|
1537
1627
|
|
|
1538
1628
|
@overload
|
|
1539
1629
|
def __get__(
|
|
1540
|
-
self:
|
|
1630
|
+
self: ComputedVar[int] | ComputedVar[float],
|
|
1541
1631
|
instance: None,
|
|
1542
1632
|
owner: Type,
|
|
1543
1633
|
) -> NumberVar: ...
|
|
1544
1634
|
|
|
1545
1635
|
@overload
|
|
1546
1636
|
def __get__(
|
|
1547
|
-
self:
|
|
1637
|
+
self: ComputedVar[str],
|
|
1548
1638
|
instance: None,
|
|
1549
1639
|
owner: Type,
|
|
1550
1640
|
) -> StringVar: ...
|
|
1551
1641
|
|
|
1552
1642
|
@overload
|
|
1553
1643
|
def __get__(
|
|
1554
|
-
self:
|
|
1644
|
+
self: ComputedVar[dict[DICT_KEY, DICT_VAL]],
|
|
1555
1645
|
instance: None,
|
|
1556
1646
|
owner: Type,
|
|
1557
1647
|
) -> ObjectVar[dict[DICT_KEY, DICT_VAL]]: ...
|
|
1558
1648
|
|
|
1559
1649
|
@overload
|
|
1560
1650
|
def __get__(
|
|
1561
|
-
self:
|
|
1651
|
+
self: ComputedVar[list[LIST_INSIDE]],
|
|
1562
1652
|
instance: None,
|
|
1563
1653
|
owner: Type,
|
|
1564
1654
|
) -> ArrayVar[list[LIST_INSIDE]]: ...
|
|
1565
1655
|
|
|
1566
1656
|
@overload
|
|
1567
1657
|
def __get__(
|
|
1568
|
-
self:
|
|
1658
|
+
self: ComputedVar[set[LIST_INSIDE]],
|
|
1569
1659
|
instance: None,
|
|
1570
1660
|
owner: Type,
|
|
1571
1661
|
) -> ArrayVar[set[LIST_INSIDE]]: ...
|
|
1572
1662
|
|
|
1573
1663
|
@overload
|
|
1574
1664
|
def __get__(
|
|
1575
|
-
self:
|
|
1665
|
+
self: ComputedVar[tuple[LIST_INSIDE, ...]],
|
|
1576
1666
|
instance: None,
|
|
1577
1667
|
owner: Type,
|
|
1578
1668
|
) -> ArrayVar[tuple[LIST_INSIDE, ...]]: ...
|
|
1579
1669
|
|
|
1580
1670
|
@overload
|
|
1581
|
-
def __get__(
|
|
1582
|
-
self, instance: None, owner: Type
|
|
1583
|
-
) -> ImmutableComputedVar[RETURN_TYPE]: ...
|
|
1671
|
+
def __get__(self, instance: None, owner: Type) -> ComputedVar[RETURN_TYPE]: ...
|
|
1584
1672
|
|
|
1585
1673
|
@overload
|
|
1586
1674
|
def __get__(self, instance: BaseState, owner: Type) -> RETURN_TYPE: ...
|
|
@@ -1599,13 +1687,13 @@ class ImmutableComputedVar(ImmutableVar[RETURN_TYPE]):
|
|
|
1599
1687
|
"""
|
|
1600
1688
|
if instance is None:
|
|
1601
1689
|
state_where_defined = owner
|
|
1602
|
-
while self.
|
|
1690
|
+
while self._js_expr in state_where_defined.inherited_vars:
|
|
1603
1691
|
state_where_defined = state_where_defined.get_parent_state()
|
|
1604
1692
|
|
|
1605
1693
|
return self._replace(
|
|
1606
|
-
|
|
1694
|
+
_js_expr=format_state_name(state_where_defined.get_full_name())
|
|
1607
1695
|
+ "."
|
|
1608
|
-
+ self.
|
|
1696
|
+
+ self._js_expr,
|
|
1609
1697
|
merge_var_data=VarData.from_state(state_where_defined),
|
|
1610
1698
|
).guess_type()
|
|
1611
1699
|
|
|
@@ -1705,7 +1793,7 @@ class ImmutableComputedVar(ImmutableVar[RETURN_TYPE]):
|
|
|
1705
1793
|
)
|
|
1706
1794
|
# recurse into property fget functions
|
|
1707
1795
|
elif isinstance(ref_obj, property) and not isinstance(
|
|
1708
|
-
ref_obj,
|
|
1796
|
+
ref_obj, ComputedVar
|
|
1709
1797
|
):
|
|
1710
1798
|
d.update(
|
|
1711
1799
|
self._deps(
|
|
@@ -1773,7 +1861,7 @@ class ImmutableComputedVar(ImmutableVar[RETURN_TYPE]):
|
|
|
1773
1861
|
return self._fget
|
|
1774
1862
|
|
|
1775
1863
|
|
|
1776
|
-
class DynamicRouteVar(
|
|
1864
|
+
class DynamicRouteVar(ComputedVar[Union[str, List[str]]]):
|
|
1777
1865
|
"""A ComputedVar that represents a dynamic route."""
|
|
1778
1866
|
|
|
1779
1867
|
pass
|
|
@@ -1784,45 +1872,41 @@ if TYPE_CHECKING:
|
|
|
1784
1872
|
|
|
1785
1873
|
|
|
1786
1874
|
@overload
|
|
1787
|
-
def
|
|
1875
|
+
def computed_var(
|
|
1788
1876
|
fget: None = None,
|
|
1789
1877
|
initial_value: Any | types.Unset = types.Unset(),
|
|
1790
1878
|
cache: bool = False,
|
|
1791
|
-
deps: Optional[List[Union[str,
|
|
1879
|
+
deps: Optional[List[Union[str, Var]]] = None,
|
|
1792
1880
|
auto_deps: bool = True,
|
|
1793
1881
|
interval: Optional[Union[datetime.timedelta, int]] = None,
|
|
1794
1882
|
backend: bool | None = None,
|
|
1795
1883
|
**kwargs,
|
|
1796
|
-
) -> Callable[
|
|
1797
|
-
[Callable[[BASE_STATE], RETURN_TYPE]], ImmutableComputedVar[RETURN_TYPE]
|
|
1798
|
-
]: ...
|
|
1884
|
+
) -> Callable[[Callable[[BASE_STATE], RETURN_TYPE]], ComputedVar[RETURN_TYPE]]: ...
|
|
1799
1885
|
|
|
1800
1886
|
|
|
1801
1887
|
@overload
|
|
1802
|
-
def
|
|
1888
|
+
def computed_var(
|
|
1803
1889
|
fget: Callable[[BASE_STATE], RETURN_TYPE],
|
|
1804
1890
|
initial_value: RETURN_TYPE | types.Unset = types.Unset(),
|
|
1805
1891
|
cache: bool = False,
|
|
1806
|
-
deps: Optional[List[Union[str,
|
|
1892
|
+
deps: Optional[List[Union[str, Var]]] = None,
|
|
1807
1893
|
auto_deps: bool = True,
|
|
1808
1894
|
interval: Optional[Union[datetime.timedelta, int]] = None,
|
|
1809
1895
|
backend: bool | None = None,
|
|
1810
1896
|
**kwargs,
|
|
1811
|
-
) ->
|
|
1897
|
+
) -> ComputedVar[RETURN_TYPE]: ...
|
|
1812
1898
|
|
|
1813
1899
|
|
|
1814
|
-
def
|
|
1900
|
+
def computed_var(
|
|
1815
1901
|
fget: Callable[[BASE_STATE], Any] | None = None,
|
|
1816
1902
|
initial_value: Any | types.Unset = types.Unset(),
|
|
1817
1903
|
cache: bool = False,
|
|
1818
|
-
deps: Optional[List[Union[str,
|
|
1904
|
+
deps: Optional[List[Union[str, Var]]] = None,
|
|
1819
1905
|
auto_deps: bool = True,
|
|
1820
1906
|
interval: Optional[Union[datetime.timedelta, int]] = None,
|
|
1821
1907
|
backend: bool | None = None,
|
|
1822
1908
|
**kwargs,
|
|
1823
|
-
) ->
|
|
1824
|
-
ImmutableComputedVar | Callable[[Callable[[BASE_STATE], Any]], ImmutableComputedVar]
|
|
1825
|
-
):
|
|
1909
|
+
) -> ComputedVar | Callable[[Callable[[BASE_STATE], Any]], ComputedVar]:
|
|
1826
1910
|
"""A ComputedVar decorator with or without kwargs.
|
|
1827
1911
|
|
|
1828
1912
|
Args:
|
|
@@ -1849,10 +1933,10 @@ def immutable_computed_var(
|
|
|
1849
1933
|
raise VarDependencyError("Cannot track dependencies without caching.")
|
|
1850
1934
|
|
|
1851
1935
|
if fget is not None:
|
|
1852
|
-
return
|
|
1936
|
+
return ComputedVar(fget, cache=cache)
|
|
1853
1937
|
|
|
1854
|
-
def wrapper(fget: Callable[[BASE_STATE], Any]) ->
|
|
1855
|
-
return
|
|
1938
|
+
def wrapper(fget: Callable[[BASE_STATE], Any]) -> ComputedVar:
|
|
1939
|
+
return ComputedVar(
|
|
1856
1940
|
fget,
|
|
1857
1941
|
initial_value=initial_value,
|
|
1858
1942
|
cache=cache,
|
|
@@ -1869,7 +1953,7 @@ def immutable_computed_var(
|
|
|
1869
1953
|
RETURN = TypeVar("RETURN")
|
|
1870
1954
|
|
|
1871
1955
|
|
|
1872
|
-
class CustomVarOperationReturn(
|
|
1956
|
+
class CustomVarOperationReturn(Var[RETURN]):
|
|
1873
1957
|
"""Base class for custom var operations."""
|
|
1874
1958
|
|
|
1875
1959
|
@classmethod
|
|
@@ -1890,7 +1974,7 @@ class CustomVarOperationReturn(ImmutableVar[RETURN]):
|
|
|
1890
1974
|
The CustomVarOperation.
|
|
1891
1975
|
"""
|
|
1892
1976
|
return CustomVarOperationReturn(
|
|
1893
|
-
|
|
1977
|
+
_js_expr=js_expression,
|
|
1894
1978
|
_var_type=_var_type or Any,
|
|
1895
1979
|
_var_data=_var_data,
|
|
1896
1980
|
)
|
|
@@ -1917,12 +2001,10 @@ def var_operation_return(
|
|
|
1917
2001
|
frozen=True,
|
|
1918
2002
|
**{"slots": True} if sys.version_info >= (3, 10) else {},
|
|
1919
2003
|
)
|
|
1920
|
-
class CustomVarOperation(CachedVarOperation,
|
|
2004
|
+
class CustomVarOperation(CachedVarOperation, Var[T]):
|
|
1921
2005
|
"""Base class for custom var operations."""
|
|
1922
2006
|
|
|
1923
|
-
_args: Tuple[Tuple[str,
|
|
1924
|
-
default_factory=tuple
|
|
1925
|
-
)
|
|
2007
|
+
_args: Tuple[Tuple[str, Var], ...] = dataclasses.field(default_factory=tuple)
|
|
1926
2008
|
|
|
1927
2009
|
_return: CustomVarOperationReturn[T] = dataclasses.field(
|
|
1928
2010
|
default_factory=lambda: CustomVarOperationReturn.create("")
|
|
@@ -1956,7 +2038,7 @@ class CustomVarOperation(CachedVarOperation, ImmutableVar[T]):
|
|
|
1956
2038
|
@classmethod
|
|
1957
2039
|
def create(
|
|
1958
2040
|
cls,
|
|
1959
|
-
args: Tuple[Tuple[str,
|
|
2041
|
+
args: Tuple[Tuple[str, Var], ...],
|
|
1960
2042
|
return_var: CustomVarOperationReturn[T],
|
|
1961
2043
|
_var_data: VarData | None = None,
|
|
1962
2044
|
) -> CustomVarOperation[T]:
|
|
@@ -1971,7 +2053,7 @@ class CustomVarOperation(CachedVarOperation, ImmutableVar[T]):
|
|
|
1971
2053
|
The CustomVarOperation.
|
|
1972
2054
|
"""
|
|
1973
2055
|
return CustomVarOperation(
|
|
1974
|
-
|
|
2056
|
+
_js_expr="",
|
|
1975
2057
|
_var_type=return_var._var_type,
|
|
1976
2058
|
_var_data=_var_data,
|
|
1977
2059
|
_args=args,
|
|
@@ -1979,7 +2061,7 @@ class CustomVarOperation(CachedVarOperation, ImmutableVar[T]):
|
|
|
1979
2061
|
)
|
|
1980
2062
|
|
|
1981
2063
|
|
|
1982
|
-
class NoneVar(
|
|
2064
|
+
class NoneVar(Var[None]):
|
|
1983
2065
|
"""A var representing None."""
|
|
1984
2066
|
|
|
1985
2067
|
|
|
@@ -2008,7 +2090,7 @@ class LiteralNoneVar(LiteralVar, NoneVar):
|
|
|
2008
2090
|
The var.
|
|
2009
2091
|
"""
|
|
2010
2092
|
return LiteralNoneVar(
|
|
2011
|
-
|
|
2093
|
+
_js_expr="null",
|
|
2012
2094
|
_var_type=None,
|
|
2013
2095
|
_var_data=_var_data,
|
|
2014
2096
|
)
|
|
@@ -2051,7 +2133,7 @@ class ToNoneOperation(CachedVarOperation, NoneVar):
|
|
|
2051
2133
|
The ToNoneOperation.
|
|
2052
2134
|
"""
|
|
2053
2135
|
return ToNoneOperation(
|
|
2054
|
-
|
|
2136
|
+
_js_expr="",
|
|
2055
2137
|
_var_type=None,
|
|
2056
2138
|
_var_data=_var_data,
|
|
2057
2139
|
_original_var=var,
|
|
@@ -2063,7 +2145,7 @@ class ToNoneOperation(CachedVarOperation, NoneVar):
|
|
|
2063
2145
|
frozen=True,
|
|
2064
2146
|
**{"slots": True} if sys.version_info >= (3, 10) else {},
|
|
2065
2147
|
)
|
|
2066
|
-
class StateOperation(CachedVarOperation,
|
|
2148
|
+
class StateOperation(CachedVarOperation, Var):
|
|
2067
2149
|
"""A var operation that accesses a field on an object."""
|
|
2068
2150
|
|
|
2069
2151
|
_state_name: str = dataclasses.field(default="")
|
|
@@ -2087,7 +2169,7 @@ class StateOperation(CachedVarOperation, ImmutableVar):
|
|
|
2087
2169
|
Returns:
|
|
2088
2170
|
The attribute.
|
|
2089
2171
|
"""
|
|
2090
|
-
if name == "
|
|
2172
|
+
if name == "_js_expr":
|
|
2091
2173
|
return self._cached_var_name
|
|
2092
2174
|
|
|
2093
2175
|
return getattr(self._field, name)
|
|
@@ -2096,7 +2178,7 @@ class StateOperation(CachedVarOperation, ImmutableVar):
|
|
|
2096
2178
|
def create(
|
|
2097
2179
|
cls,
|
|
2098
2180
|
state_name: str,
|
|
2099
|
-
field:
|
|
2181
|
+
field: Var,
|
|
2100
2182
|
_var_data: VarData | None = None,
|
|
2101
2183
|
) -> StateOperation:
|
|
2102
2184
|
"""Create a DotOperation.
|
|
@@ -2110,7 +2192,7 @@ class StateOperation(CachedVarOperation, ImmutableVar):
|
|
|
2110
2192
|
The DotOperation.
|
|
2111
2193
|
"""
|
|
2112
2194
|
return StateOperation(
|
|
2113
|
-
|
|
2195
|
+
_js_expr="",
|
|
2114
2196
|
_var_type=field._var_type,
|
|
2115
2197
|
_var_data=_var_data,
|
|
2116
2198
|
_state_name=state_name,
|
|
@@ -2134,7 +2216,7 @@ class ToOperation:
|
|
|
2134
2216
|
|
|
2135
2217
|
def __post_init__(self):
|
|
2136
2218
|
"""Post initialization."""
|
|
2137
|
-
object.__delattr__(self, "
|
|
2219
|
+
object.__delattr__(self, "_js_expr")
|
|
2138
2220
|
|
|
2139
2221
|
def __hash__(self) -> int:
|
|
2140
2222
|
"""Calculate the hash value of the object.
|
|
@@ -2173,8 +2255,287 @@ class ToOperation:
|
|
|
2173
2255
|
The ToOperation.
|
|
2174
2256
|
"""
|
|
2175
2257
|
return cls(
|
|
2176
|
-
|
|
2258
|
+
_js_expr="", # type: ignore
|
|
2177
2259
|
_var_data=_var_data, # type: ignore
|
|
2178
2260
|
_var_type=_var_type or cls._default_var_type, # type: ignore
|
|
2179
2261
|
_original=value, # type: ignore
|
|
2180
2262
|
)
|
|
2263
|
+
|
|
2264
|
+
|
|
2265
|
+
def get_uuid_string_var() -> Var:
|
|
2266
|
+
"""Return a Var that generates a single memoized UUID via .web/utils/state.js.
|
|
2267
|
+
|
|
2268
|
+
useMemo with an empty dependency array ensures that the generated UUID is
|
|
2269
|
+
consistent across re-renders of the component.
|
|
2270
|
+
|
|
2271
|
+
Returns:
|
|
2272
|
+
A Var that generates a UUID at runtime.
|
|
2273
|
+
"""
|
|
2274
|
+
from reflex.utils.imports import ImportVar
|
|
2275
|
+
from reflex.vars import Var
|
|
2276
|
+
|
|
2277
|
+
unique_uuid_var = get_unique_variable_name()
|
|
2278
|
+
unique_uuid_var_data = VarData(
|
|
2279
|
+
imports={
|
|
2280
|
+
f"/{constants.Dirs.STATE_PATH}": {ImportVar(tag="generateUUID")}, # type: ignore
|
|
2281
|
+
"react": "useMemo",
|
|
2282
|
+
},
|
|
2283
|
+
hooks={f"const {unique_uuid_var} = useMemo(generateUUID, [])": None},
|
|
2284
|
+
)
|
|
2285
|
+
|
|
2286
|
+
return Var(
|
|
2287
|
+
_js_expr=unique_uuid_var,
|
|
2288
|
+
_var_type=str,
|
|
2289
|
+
_var_data=unique_uuid_var_data,
|
|
2290
|
+
)
|
|
2291
|
+
|
|
2292
|
+
|
|
2293
|
+
# Set of unique variable names.
|
|
2294
|
+
USED_VARIABLES = set()
|
|
2295
|
+
|
|
2296
|
+
|
|
2297
|
+
def get_unique_variable_name() -> str:
|
|
2298
|
+
"""Get a unique variable name.
|
|
2299
|
+
|
|
2300
|
+
Returns:
|
|
2301
|
+
The unique variable name.
|
|
2302
|
+
"""
|
|
2303
|
+
name = "".join([random.choice(string.ascii_lowercase) for _ in range(8)])
|
|
2304
|
+
if name not in USED_VARIABLES:
|
|
2305
|
+
USED_VARIABLES.add(name)
|
|
2306
|
+
return name
|
|
2307
|
+
return get_unique_variable_name()
|
|
2308
|
+
|
|
2309
|
+
|
|
2310
|
+
@dataclasses.dataclass(
|
|
2311
|
+
eq=True,
|
|
2312
|
+
frozen=True,
|
|
2313
|
+
)
|
|
2314
|
+
class VarData:
|
|
2315
|
+
"""Metadata associated with a x."""
|
|
2316
|
+
|
|
2317
|
+
# The name of the enclosing state.
|
|
2318
|
+
state: str = dataclasses.field(default="")
|
|
2319
|
+
|
|
2320
|
+
# Imports needed to render this var
|
|
2321
|
+
imports: ImmutableParsedImportDict = dataclasses.field(default_factory=tuple)
|
|
2322
|
+
|
|
2323
|
+
# Hooks that need to be present in the component to render this var
|
|
2324
|
+
hooks: Tuple[str, ...] = dataclasses.field(default_factory=tuple)
|
|
2325
|
+
|
|
2326
|
+
def __init__(
|
|
2327
|
+
self,
|
|
2328
|
+
state: str = "",
|
|
2329
|
+
imports: ImportDict | ParsedImportDict | None = None,
|
|
2330
|
+
hooks: dict[str, None] | None = None,
|
|
2331
|
+
):
|
|
2332
|
+
"""Initialize the var data.
|
|
2333
|
+
|
|
2334
|
+
Args:
|
|
2335
|
+
state: The name of the enclosing state.
|
|
2336
|
+
imports: Imports needed to render this var.
|
|
2337
|
+
hooks: Hooks that need to be present in the component to render this var.
|
|
2338
|
+
"""
|
|
2339
|
+
immutable_imports: ImmutableParsedImportDict = tuple(
|
|
2340
|
+
sorted(
|
|
2341
|
+
((k, tuple(sorted(v))) for k, v in parse_imports(imports or {}).items())
|
|
2342
|
+
)
|
|
2343
|
+
)
|
|
2344
|
+
object.__setattr__(self, "state", state)
|
|
2345
|
+
object.__setattr__(self, "imports", immutable_imports)
|
|
2346
|
+
object.__setattr__(self, "hooks", tuple(hooks or {}))
|
|
2347
|
+
|
|
2348
|
+
def old_school_imports(self) -> ImportDict:
|
|
2349
|
+
"""Return the imports as a mutable dict.
|
|
2350
|
+
|
|
2351
|
+
Returns:
|
|
2352
|
+
The imports as a mutable dict.
|
|
2353
|
+
"""
|
|
2354
|
+
return dict((k, list(v)) for k, v in self.imports)
|
|
2355
|
+
|
|
2356
|
+
@classmethod
|
|
2357
|
+
def merge(cls, *others: VarData | None) -> VarData | None:
|
|
2358
|
+
"""Merge multiple var data objects.
|
|
2359
|
+
|
|
2360
|
+
Args:
|
|
2361
|
+
*others: The var data objects to merge.
|
|
2362
|
+
|
|
2363
|
+
Returns:
|
|
2364
|
+
The merged var data object.
|
|
2365
|
+
"""
|
|
2366
|
+
state = ""
|
|
2367
|
+
_imports = {}
|
|
2368
|
+
hooks = {}
|
|
2369
|
+
for var_data in others:
|
|
2370
|
+
if var_data is None:
|
|
2371
|
+
continue
|
|
2372
|
+
state = state or var_data.state
|
|
2373
|
+
_imports = imports.merge_imports(_imports, var_data.imports)
|
|
2374
|
+
hooks.update(
|
|
2375
|
+
var_data.hooks
|
|
2376
|
+
if isinstance(var_data.hooks, dict)
|
|
2377
|
+
else {k: None for k in var_data.hooks}
|
|
2378
|
+
)
|
|
2379
|
+
|
|
2380
|
+
if state or _imports or hooks:
|
|
2381
|
+
return VarData(
|
|
2382
|
+
state=state,
|
|
2383
|
+
imports=_imports,
|
|
2384
|
+
hooks=hooks,
|
|
2385
|
+
)
|
|
2386
|
+
return None
|
|
2387
|
+
|
|
2388
|
+
def __bool__(self) -> bool:
|
|
2389
|
+
"""Check if the var data is non-empty.
|
|
2390
|
+
|
|
2391
|
+
Returns:
|
|
2392
|
+
True if any field is set to a non-default value.
|
|
2393
|
+
"""
|
|
2394
|
+
return bool(self.state or self.imports or self.hooks)
|
|
2395
|
+
|
|
2396
|
+
def __eq__(self, other: Any) -> bool:
|
|
2397
|
+
"""Check if two var data objects are equal.
|
|
2398
|
+
|
|
2399
|
+
Args:
|
|
2400
|
+
other: The other var data object to compare.
|
|
2401
|
+
|
|
2402
|
+
Returns:
|
|
2403
|
+
True if all fields are equal and collapsed imports are equal.
|
|
2404
|
+
"""
|
|
2405
|
+
if not isinstance(other, VarData):
|
|
2406
|
+
return False
|
|
2407
|
+
|
|
2408
|
+
# Don't compare interpolations - that's added in by the decoder, and
|
|
2409
|
+
# not part of the vardata itself.
|
|
2410
|
+
return (
|
|
2411
|
+
self.state == other.state
|
|
2412
|
+
and self.hooks
|
|
2413
|
+
== (
|
|
2414
|
+
other.hooks if isinstance(other, VarData) else tuple(other.hooks.keys())
|
|
2415
|
+
)
|
|
2416
|
+
and imports.collapse_imports(self.imports)
|
|
2417
|
+
== imports.collapse_imports(other.imports)
|
|
2418
|
+
)
|
|
2419
|
+
|
|
2420
|
+
@classmethod
|
|
2421
|
+
def from_state(cls, state: Type[BaseState] | str) -> VarData:
|
|
2422
|
+
"""Set the state of the var.
|
|
2423
|
+
|
|
2424
|
+
Args:
|
|
2425
|
+
state: The state to set or the full name of the state.
|
|
2426
|
+
|
|
2427
|
+
Returns:
|
|
2428
|
+
The var with the set state.
|
|
2429
|
+
"""
|
|
2430
|
+
from reflex.utils import format
|
|
2431
|
+
|
|
2432
|
+
state_name = state if isinstance(state, str) else state.get_full_name()
|
|
2433
|
+
new_var_data = VarData(
|
|
2434
|
+
state=state_name,
|
|
2435
|
+
hooks={
|
|
2436
|
+
"const {0} = useContext(StateContexts.{0})".format(
|
|
2437
|
+
format.format_state_name(state_name)
|
|
2438
|
+
): None
|
|
2439
|
+
},
|
|
2440
|
+
imports={
|
|
2441
|
+
f"/{constants.Dirs.CONTEXTS_PATH}": [ImportVar(tag="StateContexts")],
|
|
2442
|
+
"react": [ImportVar(tag="useContext")],
|
|
2443
|
+
},
|
|
2444
|
+
)
|
|
2445
|
+
return new_var_data
|
|
2446
|
+
|
|
2447
|
+
|
|
2448
|
+
def _decode_var_immutable(value: str) -> tuple[VarData | None, str]:
|
|
2449
|
+
"""Decode the state name from a formatted var.
|
|
2450
|
+
|
|
2451
|
+
Args:
|
|
2452
|
+
value: The value to extract the state name from.
|
|
2453
|
+
|
|
2454
|
+
Returns:
|
|
2455
|
+
The extracted state name and the value without the state name.
|
|
2456
|
+
"""
|
|
2457
|
+
var_datas = []
|
|
2458
|
+
if isinstance(value, str):
|
|
2459
|
+
# fast path if there is no encoded VarData
|
|
2460
|
+
if constants.REFLEX_VAR_OPENING_TAG not in value:
|
|
2461
|
+
return None, value
|
|
2462
|
+
|
|
2463
|
+
offset = 0
|
|
2464
|
+
|
|
2465
|
+
# Find all tags.
|
|
2466
|
+
while m := _decode_var_pattern.search(value):
|
|
2467
|
+
start, end = m.span()
|
|
2468
|
+
value = value[:start] + value[end:]
|
|
2469
|
+
|
|
2470
|
+
serialized_data = m.group(1)
|
|
2471
|
+
|
|
2472
|
+
if serialized_data.isnumeric() or (
|
|
2473
|
+
serialized_data[0] == "-" and serialized_data[1:].isnumeric()
|
|
2474
|
+
):
|
|
2475
|
+
# This is a global immutable var.
|
|
2476
|
+
var = _global_vars[int(serialized_data)]
|
|
2477
|
+
var_data = var._get_all_var_data()
|
|
2478
|
+
|
|
2479
|
+
if var_data is not None:
|
|
2480
|
+
var_datas.append(var_data)
|
|
2481
|
+
offset += end - start
|
|
2482
|
+
|
|
2483
|
+
return VarData.merge(*var_datas) if var_datas else None, value
|
|
2484
|
+
|
|
2485
|
+
|
|
2486
|
+
# Compile regex for finding reflex var tags.
|
|
2487
|
+
_decode_var_pattern_re = (
|
|
2488
|
+
rf"{constants.REFLEX_VAR_OPENING_TAG}(.*?){constants.REFLEX_VAR_CLOSING_TAG}"
|
|
2489
|
+
)
|
|
2490
|
+
_decode_var_pattern = re.compile(_decode_var_pattern_re, flags=re.DOTALL)
|
|
2491
|
+
|
|
2492
|
+
# Defined global immutable vars.
|
|
2493
|
+
_global_vars: Dict[int, Var] = {}
|
|
2494
|
+
|
|
2495
|
+
|
|
2496
|
+
def _extract_var_data(value: Iterable) -> list[VarData | None]:
|
|
2497
|
+
"""Extract the var imports and hooks from an iterable containing a Var.
|
|
2498
|
+
|
|
2499
|
+
Args:
|
|
2500
|
+
value: The iterable to extract the VarData from
|
|
2501
|
+
|
|
2502
|
+
Returns:
|
|
2503
|
+
The extracted VarDatas.
|
|
2504
|
+
"""
|
|
2505
|
+
from reflex.style import Style
|
|
2506
|
+
from reflex.vars import Var
|
|
2507
|
+
|
|
2508
|
+
var_datas = []
|
|
2509
|
+
with contextlib.suppress(TypeError):
|
|
2510
|
+
for sub in value:
|
|
2511
|
+
if isinstance(sub, Var):
|
|
2512
|
+
var_datas.append(sub._var_data)
|
|
2513
|
+
elif not isinstance(sub, str):
|
|
2514
|
+
# Recurse into dict values.
|
|
2515
|
+
if hasattr(sub, "values") and callable(sub.values):
|
|
2516
|
+
var_datas.extend(_extract_var_data(sub.values()))
|
|
2517
|
+
# Recurse into iterable values (or dict keys).
|
|
2518
|
+
var_datas.extend(_extract_var_data(sub))
|
|
2519
|
+
|
|
2520
|
+
# Style objects should already have _var_data.
|
|
2521
|
+
if isinstance(value, Style):
|
|
2522
|
+
var_datas.append(value._var_data)
|
|
2523
|
+
else:
|
|
2524
|
+
# Recurse when value is a dict itself.
|
|
2525
|
+
values = getattr(value, "values", None)
|
|
2526
|
+
if callable(values):
|
|
2527
|
+
var_datas.extend(_extract_var_data(values()))
|
|
2528
|
+
return var_datas
|
|
2529
|
+
|
|
2530
|
+
|
|
2531
|
+
# These names were changed in reflex 0.3.0
|
|
2532
|
+
REPLACED_NAMES = {
|
|
2533
|
+
"full_name": "_var_full_name",
|
|
2534
|
+
"name": "_js_expr",
|
|
2535
|
+
"state": "_var_data.state",
|
|
2536
|
+
"type_": "_var_type",
|
|
2537
|
+
"is_local": "_var_is_local",
|
|
2538
|
+
"is_string": "_var_is_string",
|
|
2539
|
+
"set_state": "_var_set_state",
|
|
2540
|
+
"deps": "_deps",
|
|
2541
|
+
}
|