reflex 0.6.3a4__py3-none-any.whl → 0.6.4__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 +2 -2
- reflex/.templates/jinja/web/utils/context.js.jinja2 +3 -1
- reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js +3 -3
- reflex/.templates/web/components/shiki/code.js +29 -0
- reflex/.templates/web/jsconfig.json +2 -1
- reflex/.templates/web/utils/state.js +6 -4
- reflex/__init__.py +6 -3
- reflex/__init__.pyi +4 -3
- reflex/app.py +6 -5
- reflex/compiler/compiler.py +6 -7
- reflex/compiler/utils.py +8 -1
- reflex/components/base/error_boundary.py +37 -24
- reflex/components/base/error_boundary.pyi +8 -7
- reflex/components/component.py +9 -4
- reflex/components/core/banner.py +2 -2
- reflex/components/core/client_side_routing.py +1 -1
- reflex/components/core/clipboard.py +1 -1
- reflex/components/core/clipboard.pyi +1 -1
- reflex/components/core/cond.py +1 -1
- reflex/components/core/debounce.py +5 -1
- reflex/components/core/upload.py +7 -9
- reflex/components/core/upload.pyi +2 -2
- reflex/components/datadisplay/code.py +1 -1
- reflex/components/datadisplay/dataeditor.py +83 -18
- reflex/components/datadisplay/dataeditor.pyi +67 -15
- reflex/components/datadisplay/shiki_code_block.py +813 -0
- reflex/components/datadisplay/shiki_code_block.pyi +2211 -0
- reflex/components/dynamic.py +3 -3
- reflex/components/el/elements/forms.py +37 -23
- reflex/components/el/elements/forms.pyi +7 -4
- reflex/components/markdown/markdown.py +12 -1
- reflex/components/moment/moment.pyi +1 -1
- reflex/components/radix/primitives/drawer.pyi +2 -2
- reflex/components/radix/themes/base.py +2 -2
- reflex/components/radix/themes/color_mode.pyi +1 -1
- reflex/components/radix/themes/components/alert_dialog.pyi +1 -1
- reflex/components/radix/themes/components/checkbox.pyi +3 -3
- reflex/components/radix/themes/components/context_menu.pyi +1 -1
- reflex/components/radix/themes/components/dialog.pyi +2 -2
- reflex/components/radix/themes/components/dropdown_menu.pyi +2 -2
- reflex/components/radix/themes/components/hover_card.pyi +2 -2
- reflex/components/radix/themes/components/popover.pyi +1 -1
- reflex/components/radix/themes/components/radio_cards.pyi +1 -1
- reflex/components/radix/themes/components/radio_group.pyi +1 -1
- reflex/components/radix/themes/components/select.pyi +6 -6
- reflex/components/radix/themes/components/switch.pyi +1 -1
- reflex/components/radix/themes/components/tabs.pyi +2 -2
- reflex/components/radix/themes/components/tooltip.pyi +4 -2
- reflex/components/react_player/__init__.py +1 -0
- reflex/components/react_player/audio.pyi +6 -3
- reflex/components/react_player/react_player.py +12 -1
- reflex/components/react_player/react_player.pyi +11 -3
- reflex/components/react_player/video.pyi +6 -3
- reflex/components/recharts/recharts.py +2 -2
- reflex/components/sonner/toast.py +1 -1
- reflex/components/suneditor/editor.py +40 -16
- reflex/components/suneditor/editor.pyi +15 -11
- reflex/config.py +284 -20
- reflex/constants/__init__.py +2 -0
- reflex/constants/base.py +53 -31
- reflex/constants/compiler.py +2 -12
- reflex/constants/config.py +1 -2
- reflex/constants/installer.py +88 -32
- reflex/constants/style.py +1 -1
- reflex/constants/utils.py +32 -0
- reflex/custom_components/custom_components.py +3 -3
- reflex/event.py +152 -84
- reflex/experimental/__init__.py +2 -0
- reflex/experimental/client_state.py +1 -1
- reflex/experimental/layout.pyi +1 -1
- reflex/istate/storage.py +144 -0
- reflex/model.py +8 -11
- reflex/reflex.py +18 -17
- reflex/state.py +89 -151
- reflex/style.py +1 -1
- reflex/testing.py +2 -1
- reflex/utils/build.py +0 -12
- reflex/utils/exceptions.py +8 -0
- reflex/utils/exec.py +22 -4
- reflex/utils/imports.py +6 -0
- reflex/utils/net.py +2 -4
- reflex/utils/path_ops.py +7 -21
- reflex/utils/prerequisites.py +11 -17
- reflex/utils/pyi_generator.py +91 -3
- reflex/utils/registry.py +2 -6
- reflex/utils/types.py +14 -0
- reflex/vars/base.py +453 -424
- reflex/vars/function.py +6 -16
- reflex/vars/number.py +46 -67
- reflex/vars/object.py +1 -31
- reflex/vars/sequence.py +177 -47
- {reflex-0.6.3a4.dist-info → reflex-0.6.4.dist-info}/METADATA +1 -1
- {reflex-0.6.3a4.dist-info → reflex-0.6.4.dist-info}/RECORD +96 -91
- {reflex-0.6.3a4.dist-info → reflex-0.6.4.dist-info}/LICENSE +0 -0
- {reflex-0.6.3a4.dist-info → reflex-0.6.4.dist-info}/WHEEL +0 -0
- {reflex-0.6.3a4.dist-info → reflex-0.6.4.dist-info}/entry_points.txt +0 -0
reflex/vars/base.py
CHANGED
|
@@ -19,6 +19,7 @@ from typing import (
|
|
|
19
19
|
TYPE_CHECKING,
|
|
20
20
|
Any,
|
|
21
21
|
Callable,
|
|
22
|
+
ClassVar,
|
|
22
23
|
Dict,
|
|
23
24
|
FrozenSet,
|
|
24
25
|
Generic,
|
|
@@ -37,7 +38,13 @@ from typing import (
|
|
|
37
38
|
overload,
|
|
38
39
|
)
|
|
39
40
|
|
|
40
|
-
from typing_extensions import
|
|
41
|
+
from typing_extensions import (
|
|
42
|
+
ParamSpec,
|
|
43
|
+
TypeGuard,
|
|
44
|
+
deprecated,
|
|
45
|
+
get_type_hints,
|
|
46
|
+
override,
|
|
47
|
+
)
|
|
41
48
|
|
|
42
49
|
from reflex import constants
|
|
43
50
|
from reflex.base import Base
|
|
@@ -61,15 +68,13 @@ from reflex.utils.types import GenericType, Self, get_origin, has_args, unionize
|
|
|
61
68
|
if TYPE_CHECKING:
|
|
62
69
|
from reflex.state import BaseState
|
|
63
70
|
|
|
64
|
-
from .function import FunctionVar
|
|
71
|
+
from .function import FunctionVar
|
|
65
72
|
from .number import (
|
|
66
73
|
BooleanVar,
|
|
67
74
|
NumberVar,
|
|
68
|
-
ToBooleanVarOperation,
|
|
69
|
-
ToNumberVarOperation,
|
|
70
75
|
)
|
|
71
|
-
from .object import ObjectVar
|
|
72
|
-
from .sequence import ArrayVar, StringVar
|
|
76
|
+
from .object import ObjectVar
|
|
77
|
+
from .sequence import ArrayVar, StringVar
|
|
73
78
|
|
|
74
79
|
|
|
75
80
|
VAR_TYPE = TypeVar("VAR_TYPE", covariant=True)
|
|
@@ -78,6 +83,184 @@ OTHER_VAR_TYPE = TypeVar("OTHER_VAR_TYPE")
|
|
|
78
83
|
warnings.filterwarnings("ignore", message="fields may not start with an underscore")
|
|
79
84
|
|
|
80
85
|
|
|
86
|
+
@dataclasses.dataclass(
|
|
87
|
+
eq=False,
|
|
88
|
+
frozen=True,
|
|
89
|
+
)
|
|
90
|
+
class VarSubclassEntry:
|
|
91
|
+
"""Entry for a Var subclass."""
|
|
92
|
+
|
|
93
|
+
var_subclass: Type[Var]
|
|
94
|
+
to_var_subclass: Type[ToOperation]
|
|
95
|
+
python_types: Tuple[GenericType, ...]
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
_var_subclasses: List[VarSubclassEntry] = []
|
|
99
|
+
_var_literal_subclasses: List[Tuple[Type[LiteralVar], VarSubclassEntry]] = []
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@dataclasses.dataclass(
|
|
103
|
+
eq=True,
|
|
104
|
+
frozen=True,
|
|
105
|
+
)
|
|
106
|
+
class VarData:
|
|
107
|
+
"""Metadata associated with a x."""
|
|
108
|
+
|
|
109
|
+
# The name of the enclosing state.
|
|
110
|
+
state: str = dataclasses.field(default="")
|
|
111
|
+
|
|
112
|
+
# The name of the field in the state.
|
|
113
|
+
field_name: str = dataclasses.field(default="")
|
|
114
|
+
|
|
115
|
+
# Imports needed to render this var
|
|
116
|
+
imports: ImmutableParsedImportDict = dataclasses.field(default_factory=tuple)
|
|
117
|
+
|
|
118
|
+
# Hooks that need to be present in the component to render this var
|
|
119
|
+
hooks: Tuple[str, ...] = dataclasses.field(default_factory=tuple)
|
|
120
|
+
|
|
121
|
+
def __init__(
|
|
122
|
+
self,
|
|
123
|
+
state: str = "",
|
|
124
|
+
field_name: str = "",
|
|
125
|
+
imports: ImportDict | ParsedImportDict | None = None,
|
|
126
|
+
hooks: dict[str, None] | None = None,
|
|
127
|
+
):
|
|
128
|
+
"""Initialize the var data.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
state: The name of the enclosing state.
|
|
132
|
+
field_name: The name of the field in the state.
|
|
133
|
+
imports: Imports needed to render this var.
|
|
134
|
+
hooks: Hooks that need to be present in the component to render this var.
|
|
135
|
+
"""
|
|
136
|
+
immutable_imports: ImmutableParsedImportDict = tuple(
|
|
137
|
+
sorted(
|
|
138
|
+
((k, tuple(sorted(v))) for k, v in parse_imports(imports or {}).items())
|
|
139
|
+
)
|
|
140
|
+
)
|
|
141
|
+
object.__setattr__(self, "state", state)
|
|
142
|
+
object.__setattr__(self, "field_name", field_name)
|
|
143
|
+
object.__setattr__(self, "imports", immutable_imports)
|
|
144
|
+
object.__setattr__(self, "hooks", tuple(hooks or {}))
|
|
145
|
+
|
|
146
|
+
def old_school_imports(self) -> ImportDict:
|
|
147
|
+
"""Return the imports as a mutable dict.
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
The imports as a mutable dict.
|
|
151
|
+
"""
|
|
152
|
+
return dict((k, list(v)) for k, v in self.imports)
|
|
153
|
+
|
|
154
|
+
@classmethod
|
|
155
|
+
def merge(cls, *others: VarData | None) -> VarData | None:
|
|
156
|
+
"""Merge multiple var data objects.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
*others: The var data objects to merge.
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
The merged var data object.
|
|
163
|
+
"""
|
|
164
|
+
state = ""
|
|
165
|
+
field_name = ""
|
|
166
|
+
_imports = {}
|
|
167
|
+
hooks = {}
|
|
168
|
+
for var_data in others:
|
|
169
|
+
if var_data is None:
|
|
170
|
+
continue
|
|
171
|
+
state = state or var_data.state
|
|
172
|
+
field_name = field_name or var_data.field_name
|
|
173
|
+
_imports = imports.merge_imports(_imports, var_data.imports)
|
|
174
|
+
hooks.update(
|
|
175
|
+
var_data.hooks
|
|
176
|
+
if isinstance(var_data.hooks, dict)
|
|
177
|
+
else {k: None for k in var_data.hooks}
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
if state or _imports or hooks or field_name:
|
|
181
|
+
return VarData(
|
|
182
|
+
state=state,
|
|
183
|
+
field_name=field_name,
|
|
184
|
+
imports=_imports,
|
|
185
|
+
hooks=hooks,
|
|
186
|
+
)
|
|
187
|
+
return None
|
|
188
|
+
|
|
189
|
+
def __bool__(self) -> bool:
|
|
190
|
+
"""Check if the var data is non-empty.
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
True if any field is set to a non-default value.
|
|
194
|
+
"""
|
|
195
|
+
return bool(self.state or self.imports or self.hooks or self.field_name)
|
|
196
|
+
|
|
197
|
+
@classmethod
|
|
198
|
+
def from_state(cls, state: Type[BaseState] | str, field_name: str = "") -> VarData:
|
|
199
|
+
"""Set the state of the var.
|
|
200
|
+
|
|
201
|
+
Args:
|
|
202
|
+
state: The state to set or the full name of the state.
|
|
203
|
+
field_name: The name of the field in the state. Optional.
|
|
204
|
+
|
|
205
|
+
Returns:
|
|
206
|
+
The var with the set state.
|
|
207
|
+
"""
|
|
208
|
+
from reflex.utils import format
|
|
209
|
+
|
|
210
|
+
state_name = state if isinstance(state, str) else state.get_full_name()
|
|
211
|
+
return VarData(
|
|
212
|
+
state=state_name,
|
|
213
|
+
field_name=field_name,
|
|
214
|
+
hooks={
|
|
215
|
+
"const {0} = useContext(StateContexts.{0})".format(
|
|
216
|
+
format.format_state_name(state_name)
|
|
217
|
+
): None
|
|
218
|
+
},
|
|
219
|
+
imports={
|
|
220
|
+
f"$/{constants.Dirs.CONTEXTS_PATH}": [ImportVar(tag="StateContexts")],
|
|
221
|
+
"react": [ImportVar(tag="useContext")],
|
|
222
|
+
},
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def _decode_var_immutable(value: str) -> tuple[VarData | None, str]:
|
|
227
|
+
"""Decode the state name from a formatted var.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
value: The value to extract the state name from.
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
The extracted state name and the value without the state name.
|
|
234
|
+
"""
|
|
235
|
+
var_datas = []
|
|
236
|
+
if isinstance(value, str):
|
|
237
|
+
# fast path if there is no encoded VarData
|
|
238
|
+
if constants.REFLEX_VAR_OPENING_TAG not in value:
|
|
239
|
+
return None, value
|
|
240
|
+
|
|
241
|
+
offset = 0
|
|
242
|
+
|
|
243
|
+
# Find all tags.
|
|
244
|
+
while m := _decode_var_pattern.search(value):
|
|
245
|
+
start, end = m.span()
|
|
246
|
+
value = value[:start] + value[end:]
|
|
247
|
+
|
|
248
|
+
serialized_data = m.group(1)
|
|
249
|
+
|
|
250
|
+
if serialized_data.isnumeric() or (
|
|
251
|
+
serialized_data[0] == "-" and serialized_data[1:].isnumeric()
|
|
252
|
+
):
|
|
253
|
+
# This is a global immutable var.
|
|
254
|
+
var = _global_vars[int(serialized_data)]
|
|
255
|
+
var_data = var._get_all_var_data()
|
|
256
|
+
|
|
257
|
+
if var_data is not None:
|
|
258
|
+
var_datas.append(var_data)
|
|
259
|
+
offset += end - start
|
|
260
|
+
|
|
261
|
+
return VarData.merge(*var_datas) if var_datas else None, value
|
|
262
|
+
|
|
263
|
+
|
|
81
264
|
@dataclasses.dataclass(
|
|
82
265
|
eq=False,
|
|
83
266
|
frozen=True,
|
|
@@ -151,6 +334,40 @@ class Var(Generic[VAR_TYPE]):
|
|
|
151
334
|
"""
|
|
152
335
|
return False
|
|
153
336
|
|
|
337
|
+
def __init_subclass__(
|
|
338
|
+
cls, python_types: Tuple[GenericType, ...] | GenericType = types.Unset, **kwargs
|
|
339
|
+
):
|
|
340
|
+
"""Initialize the subclass.
|
|
341
|
+
|
|
342
|
+
Args:
|
|
343
|
+
python_types: The python types that the var represents.
|
|
344
|
+
**kwargs: Additional keyword arguments.
|
|
345
|
+
"""
|
|
346
|
+
super().__init_subclass__(**kwargs)
|
|
347
|
+
|
|
348
|
+
if python_types is not types.Unset:
|
|
349
|
+
python_types = (
|
|
350
|
+
python_types if isinstance(python_types, tuple) else (python_types,)
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
@dataclasses.dataclass(
|
|
354
|
+
eq=False,
|
|
355
|
+
frozen=True,
|
|
356
|
+
**{"slots": True} if sys.version_info >= (3, 10) else {},
|
|
357
|
+
)
|
|
358
|
+
class ToVarOperation(ToOperation, cls):
|
|
359
|
+
"""Base class of converting a var to another var type."""
|
|
360
|
+
|
|
361
|
+
_original: Var = dataclasses.field(
|
|
362
|
+
default=Var(_js_expr="null", _var_type=None),
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
_default_var_type: ClassVar[GenericType] = python_types[0]
|
|
366
|
+
|
|
367
|
+
ToVarOperation.__name__ = f'To{cls.__name__.removesuffix("Var")}Operation'
|
|
368
|
+
|
|
369
|
+
_var_subclasses.append(VarSubclassEntry(cls, ToVarOperation, python_types))
|
|
370
|
+
|
|
154
371
|
def __post_init__(self):
|
|
155
372
|
"""Post-initialize the var."""
|
|
156
373
|
# Decode any inline Var markup and apply it to the instance
|
|
@@ -331,35 +548,35 @@ class Var(Generic[VAR_TYPE]):
|
|
|
331
548
|
return f"{constants.REFLEX_VAR_OPENING_TAG}{hashed_var}{constants.REFLEX_VAR_CLOSING_TAG}{self._js_expr}"
|
|
332
549
|
|
|
333
550
|
@overload
|
|
334
|
-
def to(self, output: Type[StringVar]) ->
|
|
551
|
+
def to(self, output: Type[StringVar]) -> StringVar: ...
|
|
335
552
|
|
|
336
553
|
@overload
|
|
337
|
-
def to(self, output: Type[str]) ->
|
|
554
|
+
def to(self, output: Type[str]) -> StringVar: ...
|
|
338
555
|
|
|
339
556
|
@overload
|
|
340
|
-
def to(self, output: Type[BooleanVar]) ->
|
|
557
|
+
def to(self, output: Type[BooleanVar]) -> BooleanVar: ...
|
|
341
558
|
|
|
342
559
|
@overload
|
|
343
560
|
def to(
|
|
344
561
|
self, output: Type[NumberVar], var_type: type[int] | type[float] = float
|
|
345
|
-
) ->
|
|
562
|
+
) -> NumberVar: ...
|
|
346
563
|
|
|
347
564
|
@overload
|
|
348
565
|
def to(
|
|
349
566
|
self,
|
|
350
567
|
output: Type[ArrayVar],
|
|
351
568
|
var_type: type[list] | type[tuple] | type[set] = list,
|
|
352
|
-
) ->
|
|
569
|
+
) -> ArrayVar: ...
|
|
353
570
|
|
|
354
571
|
@overload
|
|
355
572
|
def to(
|
|
356
573
|
self, output: Type[ObjectVar], var_type: types.GenericType = dict
|
|
357
|
-
) ->
|
|
574
|
+
) -> ObjectVar: ...
|
|
358
575
|
|
|
359
576
|
@overload
|
|
360
577
|
def to(
|
|
361
578
|
self, output: Type[FunctionVar], var_type: Type[Callable] = Callable
|
|
362
|
-
) ->
|
|
579
|
+
) -> FunctionVar: ...
|
|
363
580
|
|
|
364
581
|
@overload
|
|
365
582
|
def to(
|
|
@@ -379,56 +596,26 @@ class Var(Generic[VAR_TYPE]):
|
|
|
379
596
|
output: The output type.
|
|
380
597
|
var_type: The type of the var.
|
|
381
598
|
|
|
382
|
-
Raises:
|
|
383
|
-
TypeError: If the var_type is not a supported type for the output.
|
|
384
|
-
|
|
385
599
|
Returns:
|
|
386
600
|
The converted var.
|
|
387
601
|
"""
|
|
388
|
-
from
|
|
389
|
-
EventChain,
|
|
390
|
-
EventChainVar,
|
|
391
|
-
EventSpec,
|
|
392
|
-
EventVar,
|
|
393
|
-
ToEventChainVarOperation,
|
|
394
|
-
ToEventVarOperation,
|
|
395
|
-
)
|
|
396
|
-
|
|
397
|
-
from .function import FunctionVar, ToFunctionOperation
|
|
398
|
-
from .number import (
|
|
399
|
-
BooleanVar,
|
|
400
|
-
NumberVar,
|
|
401
|
-
ToBooleanVarOperation,
|
|
402
|
-
ToNumberVarOperation,
|
|
403
|
-
)
|
|
404
|
-
from .object import ObjectVar, ToObjectOperation
|
|
405
|
-
from .sequence import ArrayVar, StringVar, ToArrayOperation, ToStringOperation
|
|
602
|
+
from .object import ObjectVar
|
|
406
603
|
|
|
407
604
|
base_type = var_type
|
|
408
605
|
if types.is_optional(base_type):
|
|
409
606
|
base_type = types.get_args(base_type)[0]
|
|
410
607
|
|
|
411
|
-
fixed_type = get_origin(base_type) or base_type
|
|
412
|
-
|
|
413
608
|
fixed_output_type = get_origin(output) or output
|
|
414
609
|
|
|
415
610
|
# If the first argument is a python type, we map it to the corresponding Var type.
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
if fixed_output_type in (int, float):
|
|
421
|
-
return self.to(NumberVar, output)
|
|
422
|
-
if fixed_output_type is str:
|
|
423
|
-
return self.to(StringVar, output)
|
|
424
|
-
if fixed_output_type is bool:
|
|
425
|
-
return self.to(BooleanVar, output)
|
|
611
|
+
for var_subclass in _var_subclasses[::-1]:
|
|
612
|
+
if fixed_output_type in var_subclass.python_types:
|
|
613
|
+
return self.to(var_subclass.var_subclass, output)
|
|
614
|
+
|
|
426
615
|
if fixed_output_type is None:
|
|
427
|
-
return
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
if fixed_output_type is EventChain:
|
|
431
|
-
return self.to(EventChainVar, output)
|
|
616
|
+
return get_to_operation(NoneVar).create(self) # type: ignore
|
|
617
|
+
|
|
618
|
+
# Handle fixed_output_type being Base or a dataclass.
|
|
432
619
|
try:
|
|
433
620
|
if issubclass(fixed_output_type, Base):
|
|
434
621
|
return self.to(ObjectVar, output)
|
|
@@ -440,57 +627,12 @@ class Var(Generic[VAR_TYPE]):
|
|
|
440
627
|
return self.to(ObjectVar, output)
|
|
441
628
|
|
|
442
629
|
if inspect.isclass(output):
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
if fixed_type is not None:
|
|
448
|
-
if fixed_type in types.UnionTypes:
|
|
449
|
-
inner_types = get_args(base_type)
|
|
450
|
-
if not all(issubclass(t, (int, float)) for t in inner_types):
|
|
451
|
-
raise TypeError(
|
|
452
|
-
f"Unsupported type {var_type} for NumberVar. Must be int or float."
|
|
453
|
-
)
|
|
454
|
-
|
|
455
|
-
elif not issubclass(fixed_type, (int, float)):
|
|
456
|
-
raise TypeError(
|
|
457
|
-
f"Unsupported type {var_type} for NumberVar. Must be int or float."
|
|
458
|
-
)
|
|
459
|
-
return ToNumberVarOperation.create(self, var_type or float)
|
|
460
|
-
|
|
461
|
-
if issubclass(output, ArrayVar):
|
|
462
|
-
if fixed_type is not None and not issubclass(
|
|
463
|
-
fixed_type, (list, tuple, set)
|
|
464
|
-
):
|
|
465
|
-
raise TypeError(
|
|
466
|
-
f"Unsupported type {var_type} for ArrayVar. Must be list, tuple, or set."
|
|
630
|
+
for var_subclass in _var_subclasses[::-1]:
|
|
631
|
+
if issubclass(output, var_subclass.var_subclass):
|
|
632
|
+
to_operation_return = var_subclass.to_var_subclass.create(
|
|
633
|
+
value=self, _var_type=var_type
|
|
467
634
|
)
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
if issubclass(output, StringVar):
|
|
471
|
-
return ToStringOperation.create(self, var_type or str)
|
|
472
|
-
|
|
473
|
-
if issubclass(output, EventVar):
|
|
474
|
-
return ToEventVarOperation.create(self, var_type or EventSpec)
|
|
475
|
-
|
|
476
|
-
if issubclass(output, EventChainVar):
|
|
477
|
-
return ToEventChainVarOperation.create(self, var_type or EventChain)
|
|
478
|
-
|
|
479
|
-
if issubclass(output, (ObjectVar, Base)):
|
|
480
|
-
return ToObjectOperation.create(self, var_type or dict)
|
|
481
|
-
|
|
482
|
-
if issubclass(output, FunctionVar):
|
|
483
|
-
# if fixed_type is not None and not issubclass(fixed_type, Callable):
|
|
484
|
-
# raise TypeError(
|
|
485
|
-
# f"Unsupported type {var_type} for FunctionVar. Must be Callable."
|
|
486
|
-
# )
|
|
487
|
-
return ToFunctionOperation.create(self, var_type or Callable)
|
|
488
|
-
|
|
489
|
-
if issubclass(output, NoneVar):
|
|
490
|
-
return ToNoneOperation.create(self)
|
|
491
|
-
|
|
492
|
-
if dataclasses.is_dataclass(output):
|
|
493
|
-
return ToObjectOperation.create(self, var_type or dict)
|
|
635
|
+
return to_operation_return # type: ignore
|
|
494
636
|
|
|
495
637
|
# If we can't determine the first argument, we just replace the _var_type.
|
|
496
638
|
if not issubclass(output, Var) or var_type is None:
|
|
@@ -508,6 +650,18 @@ class Var(Generic[VAR_TYPE]):
|
|
|
508
650
|
|
|
509
651
|
return self
|
|
510
652
|
|
|
653
|
+
@overload
|
|
654
|
+
def guess_type(self: Var[str]) -> StringVar: ...
|
|
655
|
+
|
|
656
|
+
@overload
|
|
657
|
+
def guess_type(self: Var[bool]) -> BooleanVar: ...
|
|
658
|
+
|
|
659
|
+
@overload
|
|
660
|
+
def guess_type(self: Var[int] | Var[float] | Var[int | float]) -> NumberVar: ...
|
|
661
|
+
|
|
662
|
+
@overload
|
|
663
|
+
def guess_type(self) -> Self: ...
|
|
664
|
+
|
|
511
665
|
def guess_type(self) -> Var:
|
|
512
666
|
"""Guesses the type of the variable based on its `_var_type` attribute.
|
|
513
667
|
|
|
@@ -517,11 +671,8 @@ class Var(Generic[VAR_TYPE]):
|
|
|
517
671
|
Raises:
|
|
518
672
|
TypeError: If the type is not supported for guessing.
|
|
519
673
|
"""
|
|
520
|
-
from
|
|
521
|
-
|
|
522
|
-
from .number import BooleanVar, NumberVar
|
|
674
|
+
from .number import NumberVar
|
|
523
675
|
from .object import ObjectVar
|
|
524
|
-
from .sequence import ArrayVar, StringVar
|
|
525
676
|
|
|
526
677
|
var_type = self._var_type
|
|
527
678
|
if var_type is None:
|
|
@@ -558,20 +709,13 @@ class Var(Generic[VAR_TYPE]):
|
|
|
558
709
|
if not inspect.isclass(fixed_type):
|
|
559
710
|
raise TypeError(f"Unsupported type {var_type} for guess_type.")
|
|
560
711
|
|
|
561
|
-
if
|
|
562
|
-
return self.to(
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
return self.to(ArrayVar, self._var_type)
|
|
569
|
-
if issubclass(fixed_type, str):
|
|
570
|
-
return self.to(StringVar, self._var_type)
|
|
571
|
-
if issubclass(fixed_type, EventSpec):
|
|
572
|
-
return self.to(EventVar, self._var_type)
|
|
573
|
-
if issubclass(fixed_type, EventChain):
|
|
574
|
-
return self.to(EventChainVar, self._var_type)
|
|
712
|
+
if fixed_type is None:
|
|
713
|
+
return self.to(None)
|
|
714
|
+
|
|
715
|
+
for var_subclass in _var_subclasses[::-1]:
|
|
716
|
+
if issubclass(fixed_type, var_subclass.python_types):
|
|
717
|
+
return self.to(var_subclass.var_subclass, self._var_type)
|
|
718
|
+
|
|
575
719
|
try:
|
|
576
720
|
if issubclass(fixed_type, Base):
|
|
577
721
|
return self.to(ObjectVar, self._var_type)
|
|
@@ -782,16 +926,23 @@ class Var(Generic[VAR_TYPE]):
|
|
|
782
926
|
"""
|
|
783
927
|
return ~self.bool()
|
|
784
928
|
|
|
785
|
-
def to_string(self):
|
|
929
|
+
def to_string(self, use_json: bool = True) -> StringVar:
|
|
786
930
|
"""Convert the var to a string.
|
|
787
931
|
|
|
932
|
+
Args:
|
|
933
|
+
use_json: Whether to use JSON stringify. If False, uses Object.prototype.toString.
|
|
934
|
+
|
|
788
935
|
Returns:
|
|
789
936
|
The string var.
|
|
790
937
|
"""
|
|
791
|
-
from .function import JSON_STRINGIFY
|
|
938
|
+
from .function import JSON_STRINGIFY, PROTOTYPE_TO_STRING
|
|
792
939
|
from .sequence import StringVar
|
|
793
940
|
|
|
794
|
-
return
|
|
941
|
+
return (
|
|
942
|
+
JSON_STRINGIFY.call(self).to(StringVar)
|
|
943
|
+
if use_json
|
|
944
|
+
else PROTOTYPE_TO_STRING.call(self).to(StringVar)
|
|
945
|
+
)
|
|
795
946
|
|
|
796
947
|
def as_ref(self) -> Var:
|
|
797
948
|
"""Get a reference to the var.
|
|
@@ -805,7 +956,7 @@ class Var(Generic[VAR_TYPE]):
|
|
|
805
956
|
_js_expr="refs",
|
|
806
957
|
_var_data=VarData(
|
|
807
958
|
imports={
|
|
808
|
-
f"
|
|
959
|
+
f"$/{constants.Dirs.STATE_PATH}": [imports.ImportVar(tag="refs")]
|
|
809
960
|
}
|
|
810
961
|
),
|
|
811
962
|
).to(ObjectVar, Dict[str, str])
|
|
@@ -1017,9 +1168,129 @@ class Var(Generic[VAR_TYPE]):
|
|
|
1017
1168
|
OUTPUT = TypeVar("OUTPUT", bound=Var)
|
|
1018
1169
|
|
|
1019
1170
|
|
|
1171
|
+
class ToOperation:
|
|
1172
|
+
"""A var operation that converts a var to another type."""
|
|
1173
|
+
|
|
1174
|
+
def __getattr__(self, name: str) -> Any:
|
|
1175
|
+
"""Get an attribute of the var.
|
|
1176
|
+
|
|
1177
|
+
Args:
|
|
1178
|
+
name: The name of the attribute.
|
|
1179
|
+
|
|
1180
|
+
Returns:
|
|
1181
|
+
The attribute of the var.
|
|
1182
|
+
"""
|
|
1183
|
+
from .object import ObjectVar
|
|
1184
|
+
|
|
1185
|
+
if isinstance(self, ObjectVar) and name != "_js_expr":
|
|
1186
|
+
return ObjectVar.__getattr__(self, name)
|
|
1187
|
+
return getattr(self._original, name)
|
|
1188
|
+
|
|
1189
|
+
def __post_init__(self):
|
|
1190
|
+
"""Post initialization."""
|
|
1191
|
+
object.__delattr__(self, "_js_expr")
|
|
1192
|
+
|
|
1193
|
+
def __hash__(self) -> int:
|
|
1194
|
+
"""Calculate the hash value of the object.
|
|
1195
|
+
|
|
1196
|
+
Returns:
|
|
1197
|
+
int: The hash value of the object.
|
|
1198
|
+
"""
|
|
1199
|
+
return hash(self._original)
|
|
1200
|
+
|
|
1201
|
+
def _get_all_var_data(self) -> VarData | None:
|
|
1202
|
+
"""Get all the var data.
|
|
1203
|
+
|
|
1204
|
+
Returns:
|
|
1205
|
+
The var data.
|
|
1206
|
+
"""
|
|
1207
|
+
return VarData.merge(
|
|
1208
|
+
self._original._get_all_var_data(),
|
|
1209
|
+
self._var_data, # type: ignore
|
|
1210
|
+
)
|
|
1211
|
+
|
|
1212
|
+
@classmethod
|
|
1213
|
+
def create(
|
|
1214
|
+
cls,
|
|
1215
|
+
value: Var,
|
|
1216
|
+
_var_type: GenericType | None = None,
|
|
1217
|
+
_var_data: VarData | None = None,
|
|
1218
|
+
):
|
|
1219
|
+
"""Create a ToOperation.
|
|
1220
|
+
|
|
1221
|
+
Args:
|
|
1222
|
+
value: The value of the var.
|
|
1223
|
+
_var_type: The type of the Var.
|
|
1224
|
+
_var_data: Additional hooks and imports associated with the Var.
|
|
1225
|
+
|
|
1226
|
+
Returns:
|
|
1227
|
+
The ToOperation.
|
|
1228
|
+
"""
|
|
1229
|
+
return cls(
|
|
1230
|
+
_js_expr="", # type: ignore
|
|
1231
|
+
_var_data=_var_data, # type: ignore
|
|
1232
|
+
_var_type=_var_type or cls._default_var_type, # type: ignore
|
|
1233
|
+
_original=value, # type: ignore
|
|
1234
|
+
)
|
|
1235
|
+
|
|
1236
|
+
|
|
1020
1237
|
class LiteralVar(Var):
|
|
1021
1238
|
"""Base class for immutable literal vars."""
|
|
1022
1239
|
|
|
1240
|
+
def __init_subclass__(cls, **kwargs):
|
|
1241
|
+
"""Initialize the subclass.
|
|
1242
|
+
|
|
1243
|
+
Args:
|
|
1244
|
+
**kwargs: Additional keyword arguments.
|
|
1245
|
+
|
|
1246
|
+
Raises:
|
|
1247
|
+
TypeError: If the LiteralVar subclass does not have a corresponding Var subclass.
|
|
1248
|
+
"""
|
|
1249
|
+
super().__init_subclass__(**kwargs)
|
|
1250
|
+
|
|
1251
|
+
bases = cls.__bases__
|
|
1252
|
+
|
|
1253
|
+
bases_normalized = [
|
|
1254
|
+
base if inspect.isclass(base) else get_origin(base) for base in bases
|
|
1255
|
+
]
|
|
1256
|
+
|
|
1257
|
+
possible_bases = [
|
|
1258
|
+
base
|
|
1259
|
+
for base in bases_normalized
|
|
1260
|
+
if issubclass(base, Var) and base != LiteralVar
|
|
1261
|
+
]
|
|
1262
|
+
|
|
1263
|
+
if not possible_bases:
|
|
1264
|
+
raise TypeError(
|
|
1265
|
+
f"LiteralVar subclass {cls} must have a base class that is a subclass of Var and not LiteralVar."
|
|
1266
|
+
)
|
|
1267
|
+
|
|
1268
|
+
var_subclasses = [
|
|
1269
|
+
var_subclass
|
|
1270
|
+
for var_subclass in _var_subclasses
|
|
1271
|
+
if var_subclass.var_subclass in possible_bases
|
|
1272
|
+
]
|
|
1273
|
+
|
|
1274
|
+
if not var_subclasses:
|
|
1275
|
+
raise TypeError(
|
|
1276
|
+
f"LiteralVar {cls} must have a base class annotated with `python_types`."
|
|
1277
|
+
)
|
|
1278
|
+
|
|
1279
|
+
if len(var_subclasses) != 1:
|
|
1280
|
+
raise TypeError(
|
|
1281
|
+
f"LiteralVar {cls} must have exactly one base class annotated with `python_types`."
|
|
1282
|
+
)
|
|
1283
|
+
|
|
1284
|
+
var_subclass = var_subclasses[0]
|
|
1285
|
+
|
|
1286
|
+
# Remove the old subclass, happens because __init_subclass__ is called twice
|
|
1287
|
+
# for each subclass. This is because of __slots__ in dataclasses.
|
|
1288
|
+
for var_literal_subclass in list(_var_literal_subclasses):
|
|
1289
|
+
if var_literal_subclass[1] is var_subclass:
|
|
1290
|
+
_var_literal_subclasses.remove(var_literal_subclass)
|
|
1291
|
+
|
|
1292
|
+
_var_literal_subclasses.append((cls, var_subclass))
|
|
1293
|
+
|
|
1023
1294
|
@classmethod
|
|
1024
1295
|
def create(
|
|
1025
1296
|
cls,
|
|
@@ -1038,50 +1309,21 @@ class LiteralVar(Var):
|
|
|
1038
1309
|
Raises:
|
|
1039
1310
|
TypeError: If the value is not a supported type for LiteralVar.
|
|
1040
1311
|
"""
|
|
1041
|
-
from .number import LiteralBooleanVar, LiteralNumberVar
|
|
1042
1312
|
from .object import LiteralObjectVar
|
|
1043
|
-
from .sequence import
|
|
1313
|
+
from .sequence import LiteralStringVar
|
|
1044
1314
|
|
|
1045
1315
|
if isinstance(value, Var):
|
|
1046
1316
|
if _var_data is None:
|
|
1047
1317
|
return value
|
|
1048
1318
|
return value._replace(merge_var_data=_var_data)
|
|
1049
1319
|
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
if isinstance(value, bool):
|
|
1054
|
-
return LiteralBooleanVar.create(value, _var_data=_var_data)
|
|
1055
|
-
|
|
1056
|
-
if isinstance(value, (int, float)):
|
|
1057
|
-
return LiteralNumberVar.create(value, _var_data=_var_data)
|
|
1058
|
-
|
|
1059
|
-
if isinstance(value, dict):
|
|
1060
|
-
return LiteralObjectVar.create(value, _var_data=_var_data)
|
|
1320
|
+
for literal_subclass, var_subclass in _var_literal_subclasses[::-1]:
|
|
1321
|
+
if isinstance(value, var_subclass.python_types):
|
|
1322
|
+
return literal_subclass.create(value, _var_data=_var_data)
|
|
1061
1323
|
|
|
1062
|
-
|
|
1063
|
-
return LiteralArrayVar.create(value, _var_data=_var_data)
|
|
1064
|
-
|
|
1065
|
-
if value is None:
|
|
1066
|
-
return LiteralNoneVar.create(_var_data=_var_data)
|
|
1067
|
-
|
|
1068
|
-
from reflex.event import (
|
|
1069
|
-
EventChain,
|
|
1070
|
-
EventHandler,
|
|
1071
|
-
EventSpec,
|
|
1072
|
-
LiteralEventChainVar,
|
|
1073
|
-
LiteralEventVar,
|
|
1074
|
-
)
|
|
1324
|
+
from reflex.event import EventHandler
|
|
1075
1325
|
from reflex.utils.format import get_event_handler_parts
|
|
1076
1326
|
|
|
1077
|
-
from .object import LiteralObjectVar
|
|
1078
|
-
|
|
1079
|
-
if isinstance(value, EventSpec):
|
|
1080
|
-
return LiteralEventVar.create(value, _var_data=_var_data)
|
|
1081
|
-
|
|
1082
|
-
if isinstance(value, EventChain):
|
|
1083
|
-
return LiteralEventChainVar.create(value, _var_data=_var_data)
|
|
1084
|
-
|
|
1085
1327
|
if isinstance(value, EventHandler):
|
|
1086
1328
|
return Var(_js_expr=".".join(filter(None, get_event_handler_parts(value))))
|
|
1087
1329
|
|
|
@@ -1155,6 +1397,22 @@ def serialize_literal(value: LiteralVar):
|
|
|
1155
1397
|
return value._var_value
|
|
1156
1398
|
|
|
1157
1399
|
|
|
1400
|
+
def get_python_literal(value: Union[LiteralVar, Any]) -> Any | None:
|
|
1401
|
+
"""Get the Python literal value.
|
|
1402
|
+
|
|
1403
|
+
Args:
|
|
1404
|
+
value: The value to get the Python literal value of.
|
|
1405
|
+
|
|
1406
|
+
Returns:
|
|
1407
|
+
The Python literal value.
|
|
1408
|
+
"""
|
|
1409
|
+
if isinstance(value, LiteralVar):
|
|
1410
|
+
return value._var_value
|
|
1411
|
+
if isinstance(value, Var):
|
|
1412
|
+
return None
|
|
1413
|
+
return value
|
|
1414
|
+
|
|
1415
|
+
|
|
1158
1416
|
P = ParamSpec("P")
|
|
1159
1417
|
T = TypeVar("T")
|
|
1160
1418
|
|
|
@@ -1205,6 +1463,12 @@ def var_operation(
|
|
|
1205
1463
|
) -> Callable[P, ObjectVar[OBJECT_TYPE]]: ...
|
|
1206
1464
|
|
|
1207
1465
|
|
|
1466
|
+
@overload
|
|
1467
|
+
def var_operation(
|
|
1468
|
+
func: Callable[P, CustomVarOperationReturn[T]],
|
|
1469
|
+
) -> Callable[P, Var[T]]: ...
|
|
1470
|
+
|
|
1471
|
+
|
|
1208
1472
|
def var_operation(
|
|
1209
1473
|
func: Callable[P, CustomVarOperationReturn[T]],
|
|
1210
1474
|
) -> Callable[P, Var[T]]:
|
|
@@ -1237,6 +1501,7 @@ def var_operation(
|
|
|
1237
1501
|
}
|
|
1238
1502
|
|
|
1239
1503
|
return CustomVarOperation.create(
|
|
1504
|
+
name=func.__name__,
|
|
1240
1505
|
args=tuple(list(args_vars.items()) + list(kwargs_vars.items())),
|
|
1241
1506
|
return_var=func(*args_vars.values(), **kwargs_vars), # type: ignore
|
|
1242
1507
|
).guess_type()
|
|
@@ -1557,8 +1822,8 @@ class ComputedVar(Var[RETURN_TYPE]):
|
|
|
1557
1822
|
"return", Any
|
|
1558
1823
|
)
|
|
1559
1824
|
|
|
1560
|
-
kwargs
|
|
1561
|
-
kwargs
|
|
1825
|
+
kwargs.setdefault("_js_expr", fget.__name__)
|
|
1826
|
+
kwargs.setdefault("_var_type", hint)
|
|
1562
1827
|
|
|
1563
1828
|
Var.__init__(
|
|
1564
1829
|
self,
|
|
@@ -1567,6 +1832,9 @@ class ComputedVar(Var[RETURN_TYPE]):
|
|
|
1567
1832
|
_var_data=kwargs.pop("_var_data", None),
|
|
1568
1833
|
)
|
|
1569
1834
|
|
|
1835
|
+
if kwargs:
|
|
1836
|
+
raise TypeError(f"Unexpected keyword arguments: {tuple(kwargs)}")
|
|
1837
|
+
|
|
1570
1838
|
if backend is None:
|
|
1571
1839
|
backend = fget.__name__.startswith("_")
|
|
1572
1840
|
|
|
@@ -2059,6 +2327,8 @@ def var_operation_return(
|
|
|
2059
2327
|
class CustomVarOperation(CachedVarOperation, Var[T]):
|
|
2060
2328
|
"""Base class for custom var operations."""
|
|
2061
2329
|
|
|
2330
|
+
_name: str = dataclasses.field(default="")
|
|
2331
|
+
|
|
2062
2332
|
_args: Tuple[Tuple[str, Var], ...] = dataclasses.field(default_factory=tuple)
|
|
2063
2333
|
|
|
2064
2334
|
_return: CustomVarOperationReturn[T] = dataclasses.field(
|
|
@@ -2093,6 +2363,7 @@ class CustomVarOperation(CachedVarOperation, Var[T]):
|
|
|
2093
2363
|
@classmethod
|
|
2094
2364
|
def create(
|
|
2095
2365
|
cls,
|
|
2366
|
+
name: str,
|
|
2096
2367
|
args: Tuple[Tuple[str, Var], ...],
|
|
2097
2368
|
return_var: CustomVarOperationReturn[T],
|
|
2098
2369
|
_var_data: VarData | None = None,
|
|
@@ -2100,6 +2371,7 @@ class CustomVarOperation(CachedVarOperation, Var[T]):
|
|
|
2100
2371
|
"""Create a CustomVarOperation.
|
|
2101
2372
|
|
|
2102
2373
|
Args:
|
|
2374
|
+
name: The name of the operation.
|
|
2103
2375
|
args: The arguments to the operation.
|
|
2104
2376
|
return_var: The return var.
|
|
2105
2377
|
_var_data: Additional hooks and imports associated with the Var.
|
|
@@ -2111,12 +2383,13 @@ class CustomVarOperation(CachedVarOperation, Var[T]):
|
|
|
2111
2383
|
_js_expr="",
|
|
2112
2384
|
_var_type=return_var._var_type,
|
|
2113
2385
|
_var_data=_var_data,
|
|
2386
|
+
_name=name,
|
|
2114
2387
|
_args=args,
|
|
2115
2388
|
_return=return_var,
|
|
2116
2389
|
)
|
|
2117
2390
|
|
|
2118
2391
|
|
|
2119
|
-
class NoneVar(Var[None]):
|
|
2392
|
+
class NoneVar(Var[None], python_types=type(None)):
|
|
2120
2393
|
"""A var representing None."""
|
|
2121
2394
|
|
|
2122
2395
|
|
|
@@ -2141,11 +2414,13 @@ class LiteralNoneVar(LiteralVar, NoneVar):
|
|
|
2141
2414
|
@classmethod
|
|
2142
2415
|
def create(
|
|
2143
2416
|
cls,
|
|
2417
|
+
value: None = None,
|
|
2144
2418
|
_var_data: VarData | None = None,
|
|
2145
2419
|
) -> LiteralNoneVar:
|
|
2146
2420
|
"""Create a var from a value.
|
|
2147
2421
|
|
|
2148
2422
|
Args:
|
|
2423
|
+
value: The value of the var. Must be None. Existed for compatibility with LiteralVar.
|
|
2149
2424
|
_var_data: Additional hooks and imports associated with the Var.
|
|
2150
2425
|
|
|
2151
2426
|
Returns:
|
|
@@ -2158,48 +2433,26 @@ class LiteralNoneVar(LiteralVar, NoneVar):
|
|
|
2158
2433
|
)
|
|
2159
2434
|
|
|
2160
2435
|
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
frozen=True,
|
|
2164
|
-
**{"slots": True} if sys.version_info >= (3, 10) else {},
|
|
2165
|
-
)
|
|
2166
|
-
class ToNoneOperation(CachedVarOperation, NoneVar):
|
|
2167
|
-
"""A var operation that converts a var to None."""
|
|
2168
|
-
|
|
2169
|
-
_original_var: Var = dataclasses.field(
|
|
2170
|
-
default_factory=lambda: LiteralNoneVar.create()
|
|
2171
|
-
)
|
|
2172
|
-
|
|
2173
|
-
@cached_property_no_lock
|
|
2174
|
-
def _cached_var_name(self) -> str:
|
|
2175
|
-
"""Get the cached var name.
|
|
2176
|
-
|
|
2177
|
-
Returns:
|
|
2178
|
-
The cached var name.
|
|
2179
|
-
"""
|
|
2180
|
-
return str(self._original_var)
|
|
2436
|
+
def get_to_operation(var_subclass: Type[Var]) -> Type[ToOperation]:
|
|
2437
|
+
"""Get the ToOperation class for a given Var subclass.
|
|
2181
2438
|
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
cls,
|
|
2185
|
-
var: Var,
|
|
2186
|
-
_var_data: VarData | None = None,
|
|
2187
|
-
) -> ToNoneOperation:
|
|
2188
|
-
"""Create a ToNoneOperation.
|
|
2439
|
+
Args:
|
|
2440
|
+
var_subclass: The Var subclass.
|
|
2189
2441
|
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
_var_data: Additional hooks and imports associated with the Var.
|
|
2442
|
+
Returns:
|
|
2443
|
+
The ToOperation class.
|
|
2193
2444
|
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2445
|
+
Raises:
|
|
2446
|
+
ValueError: If the ToOperation class cannot be found.
|
|
2447
|
+
"""
|
|
2448
|
+
possible_classes = [
|
|
2449
|
+
saved_var_subclass.to_var_subclass
|
|
2450
|
+
for saved_var_subclass in _var_subclasses
|
|
2451
|
+
if saved_var_subclass.var_subclass is var_subclass
|
|
2452
|
+
]
|
|
2453
|
+
if not possible_classes:
|
|
2454
|
+
raise ValueError(f"Could not find ToOperation for {var_subclass}.")
|
|
2455
|
+
return possible_classes[0]
|
|
2203
2456
|
|
|
2204
2457
|
|
|
2205
2458
|
@dataclasses.dataclass(
|
|
@@ -2262,68 +2515,6 @@ class StateOperation(CachedVarOperation, Var):
|
|
|
2262
2515
|
)
|
|
2263
2516
|
|
|
2264
2517
|
|
|
2265
|
-
class ToOperation:
|
|
2266
|
-
"""A var operation that converts a var to another type."""
|
|
2267
|
-
|
|
2268
|
-
def __getattr__(self, name: str) -> Any:
|
|
2269
|
-
"""Get an attribute of the var.
|
|
2270
|
-
|
|
2271
|
-
Args:
|
|
2272
|
-
name: The name of the attribute.
|
|
2273
|
-
|
|
2274
|
-
Returns:
|
|
2275
|
-
The attribute of the var.
|
|
2276
|
-
"""
|
|
2277
|
-
return getattr(object.__getattribute__(self, "_original"), name)
|
|
2278
|
-
|
|
2279
|
-
def __post_init__(self):
|
|
2280
|
-
"""Post initialization."""
|
|
2281
|
-
object.__delattr__(self, "_js_expr")
|
|
2282
|
-
|
|
2283
|
-
def __hash__(self) -> int:
|
|
2284
|
-
"""Calculate the hash value of the object.
|
|
2285
|
-
|
|
2286
|
-
Returns:
|
|
2287
|
-
int: The hash value of the object.
|
|
2288
|
-
"""
|
|
2289
|
-
return hash(object.__getattribute__(self, "_original"))
|
|
2290
|
-
|
|
2291
|
-
def _get_all_var_data(self) -> VarData | None:
|
|
2292
|
-
"""Get all the var data.
|
|
2293
|
-
|
|
2294
|
-
Returns:
|
|
2295
|
-
The var data.
|
|
2296
|
-
"""
|
|
2297
|
-
return VarData.merge(
|
|
2298
|
-
object.__getattribute__(self, "_original")._get_all_var_data(),
|
|
2299
|
-
self._var_data, # type: ignore
|
|
2300
|
-
)
|
|
2301
|
-
|
|
2302
|
-
@classmethod
|
|
2303
|
-
def create(
|
|
2304
|
-
cls,
|
|
2305
|
-
value: Var,
|
|
2306
|
-
_var_type: GenericType | None = None,
|
|
2307
|
-
_var_data: VarData | None = None,
|
|
2308
|
-
):
|
|
2309
|
-
"""Create a ToOperation.
|
|
2310
|
-
|
|
2311
|
-
Args:
|
|
2312
|
-
value: The value of the var.
|
|
2313
|
-
_var_type: The type of the Var.
|
|
2314
|
-
_var_data: Additional hooks and imports associated with the Var.
|
|
2315
|
-
|
|
2316
|
-
Returns:
|
|
2317
|
-
The ToOperation.
|
|
2318
|
-
"""
|
|
2319
|
-
return cls(
|
|
2320
|
-
_js_expr="", # type: ignore
|
|
2321
|
-
_var_data=_var_data, # type: ignore
|
|
2322
|
-
_var_type=_var_type or cls._default_var_type, # type: ignore
|
|
2323
|
-
_original=value, # type: ignore
|
|
2324
|
-
)
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
2518
|
def get_uuid_string_var() -> Var:
|
|
2328
2519
|
"""Return a Var that generates a single memoized UUID via .web/utils/state.js.
|
|
2329
2520
|
|
|
@@ -2339,7 +2530,7 @@ def get_uuid_string_var() -> Var:
|
|
|
2339
2530
|
unique_uuid_var = get_unique_variable_name()
|
|
2340
2531
|
unique_uuid_var_data = VarData(
|
|
2341
2532
|
imports={
|
|
2342
|
-
f"
|
|
2533
|
+
f"$/{constants.Dirs.STATE_PATH}": {ImportVar(tag="generateUUID")}, # type: ignore
|
|
2343
2534
|
"react": "useMemo",
|
|
2344
2535
|
},
|
|
2345
2536
|
hooks={f"const {unique_uuid_var} = useMemo(generateUUID, [])": None},
|
|
@@ -2369,168 +2560,6 @@ def get_unique_variable_name() -> str:
|
|
|
2369
2560
|
return get_unique_variable_name()
|
|
2370
2561
|
|
|
2371
2562
|
|
|
2372
|
-
@dataclasses.dataclass(
|
|
2373
|
-
eq=True,
|
|
2374
|
-
frozen=True,
|
|
2375
|
-
)
|
|
2376
|
-
class VarData:
|
|
2377
|
-
"""Metadata associated with a x."""
|
|
2378
|
-
|
|
2379
|
-
# The name of the enclosing state.
|
|
2380
|
-
state: str = dataclasses.field(default="")
|
|
2381
|
-
|
|
2382
|
-
# The name of the field in the state.
|
|
2383
|
-
field_name: str = dataclasses.field(default="")
|
|
2384
|
-
|
|
2385
|
-
# Imports needed to render this var
|
|
2386
|
-
imports: ImmutableParsedImportDict = dataclasses.field(default_factory=tuple)
|
|
2387
|
-
|
|
2388
|
-
# Hooks that need to be present in the component to render this var
|
|
2389
|
-
hooks: Tuple[str, ...] = dataclasses.field(default_factory=tuple)
|
|
2390
|
-
|
|
2391
|
-
def __init__(
|
|
2392
|
-
self,
|
|
2393
|
-
state: str = "",
|
|
2394
|
-
field_name: str = "",
|
|
2395
|
-
imports: ImportDict | ParsedImportDict | None = None,
|
|
2396
|
-
hooks: dict[str, None] | None = None,
|
|
2397
|
-
):
|
|
2398
|
-
"""Initialize the var data.
|
|
2399
|
-
|
|
2400
|
-
Args:
|
|
2401
|
-
state: The name of the enclosing state.
|
|
2402
|
-
field_name: The name of the field in the state.
|
|
2403
|
-
imports: Imports needed to render this var.
|
|
2404
|
-
hooks: Hooks that need to be present in the component to render this var.
|
|
2405
|
-
"""
|
|
2406
|
-
immutable_imports: ImmutableParsedImportDict = tuple(
|
|
2407
|
-
sorted(
|
|
2408
|
-
((k, tuple(sorted(v))) for k, v in parse_imports(imports or {}).items())
|
|
2409
|
-
)
|
|
2410
|
-
)
|
|
2411
|
-
object.__setattr__(self, "state", state)
|
|
2412
|
-
object.__setattr__(self, "field_name", field_name)
|
|
2413
|
-
object.__setattr__(self, "imports", immutable_imports)
|
|
2414
|
-
object.__setattr__(self, "hooks", tuple(hooks or {}))
|
|
2415
|
-
|
|
2416
|
-
def old_school_imports(self) -> ImportDict:
|
|
2417
|
-
"""Return the imports as a mutable dict.
|
|
2418
|
-
|
|
2419
|
-
Returns:
|
|
2420
|
-
The imports as a mutable dict.
|
|
2421
|
-
"""
|
|
2422
|
-
return dict((k, list(v)) for k, v in self.imports)
|
|
2423
|
-
|
|
2424
|
-
@classmethod
|
|
2425
|
-
def merge(cls, *others: VarData | None) -> VarData | None:
|
|
2426
|
-
"""Merge multiple var data objects.
|
|
2427
|
-
|
|
2428
|
-
Args:
|
|
2429
|
-
*others: The var data objects to merge.
|
|
2430
|
-
|
|
2431
|
-
Returns:
|
|
2432
|
-
The merged var data object.
|
|
2433
|
-
"""
|
|
2434
|
-
state = ""
|
|
2435
|
-
field_name = ""
|
|
2436
|
-
_imports = {}
|
|
2437
|
-
hooks = {}
|
|
2438
|
-
for var_data in others:
|
|
2439
|
-
if var_data is None:
|
|
2440
|
-
continue
|
|
2441
|
-
state = state or var_data.state
|
|
2442
|
-
field_name = field_name or var_data.field_name
|
|
2443
|
-
_imports = imports.merge_imports(_imports, var_data.imports)
|
|
2444
|
-
hooks.update(
|
|
2445
|
-
var_data.hooks
|
|
2446
|
-
if isinstance(var_data.hooks, dict)
|
|
2447
|
-
else {k: None for k in var_data.hooks}
|
|
2448
|
-
)
|
|
2449
|
-
|
|
2450
|
-
if state or _imports or hooks or field_name:
|
|
2451
|
-
return VarData(
|
|
2452
|
-
state=state,
|
|
2453
|
-
field_name=field_name,
|
|
2454
|
-
imports=_imports,
|
|
2455
|
-
hooks=hooks,
|
|
2456
|
-
)
|
|
2457
|
-
return None
|
|
2458
|
-
|
|
2459
|
-
def __bool__(self) -> bool:
|
|
2460
|
-
"""Check if the var data is non-empty.
|
|
2461
|
-
|
|
2462
|
-
Returns:
|
|
2463
|
-
True if any field is set to a non-default value.
|
|
2464
|
-
"""
|
|
2465
|
-
return bool(self.state or self.imports or self.hooks or self.field_name)
|
|
2466
|
-
|
|
2467
|
-
@classmethod
|
|
2468
|
-
def from_state(cls, state: Type[BaseState] | str, field_name: str = "") -> VarData:
|
|
2469
|
-
"""Set the state of the var.
|
|
2470
|
-
|
|
2471
|
-
Args:
|
|
2472
|
-
state: The state to set or the full name of the state.
|
|
2473
|
-
field_name: The name of the field in the state. Optional.
|
|
2474
|
-
|
|
2475
|
-
Returns:
|
|
2476
|
-
The var with the set state.
|
|
2477
|
-
"""
|
|
2478
|
-
from reflex.utils import format
|
|
2479
|
-
|
|
2480
|
-
state_name = state if isinstance(state, str) else state.get_full_name()
|
|
2481
|
-
return VarData(
|
|
2482
|
-
state=state_name,
|
|
2483
|
-
field_name=field_name,
|
|
2484
|
-
hooks={
|
|
2485
|
-
"const {0} = useContext(StateContexts.{0})".format(
|
|
2486
|
-
format.format_state_name(state_name)
|
|
2487
|
-
): None
|
|
2488
|
-
},
|
|
2489
|
-
imports={
|
|
2490
|
-
f"/{constants.Dirs.CONTEXTS_PATH}": [ImportVar(tag="StateContexts")],
|
|
2491
|
-
"react": [ImportVar(tag="useContext")],
|
|
2492
|
-
},
|
|
2493
|
-
)
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
def _decode_var_immutable(value: str) -> tuple[VarData | None, str]:
|
|
2497
|
-
"""Decode the state name from a formatted var.
|
|
2498
|
-
|
|
2499
|
-
Args:
|
|
2500
|
-
value: The value to extract the state name from.
|
|
2501
|
-
|
|
2502
|
-
Returns:
|
|
2503
|
-
The extracted state name and the value without the state name.
|
|
2504
|
-
"""
|
|
2505
|
-
var_datas = []
|
|
2506
|
-
if isinstance(value, str):
|
|
2507
|
-
# fast path if there is no encoded VarData
|
|
2508
|
-
if constants.REFLEX_VAR_OPENING_TAG not in value:
|
|
2509
|
-
return None, value
|
|
2510
|
-
|
|
2511
|
-
offset = 0
|
|
2512
|
-
|
|
2513
|
-
# Find all tags.
|
|
2514
|
-
while m := _decode_var_pattern.search(value):
|
|
2515
|
-
start, end = m.span()
|
|
2516
|
-
value = value[:start] + value[end:]
|
|
2517
|
-
|
|
2518
|
-
serialized_data = m.group(1)
|
|
2519
|
-
|
|
2520
|
-
if serialized_data.isnumeric() or (
|
|
2521
|
-
serialized_data[0] == "-" and serialized_data[1:].isnumeric()
|
|
2522
|
-
):
|
|
2523
|
-
# This is a global immutable var.
|
|
2524
|
-
var = _global_vars[int(serialized_data)]
|
|
2525
|
-
var_data = var._get_all_var_data()
|
|
2526
|
-
|
|
2527
|
-
if var_data is not None:
|
|
2528
|
-
var_datas.append(var_data)
|
|
2529
|
-
offset += end - start
|
|
2530
|
-
|
|
2531
|
-
return VarData.merge(*var_datas) if var_datas else None, value
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
2563
|
# Compile regex for finding reflex var tags.
|
|
2535
2564
|
_decode_var_pattern_re = (
|
|
2536
2565
|
rf"{constants.REFLEX_VAR_OPENING_TAG}(.*?){constants.REFLEX_VAR_CLOSING_TAG}"
|