reflex 0.6.0a3__py3-none-any.whl → 0.6.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of reflex might be problematic. Click here for more details.
- reflex/.templates/jinja/custom_components/pyproject.toml.jinja2 +1 -1
- reflex/.templates/jinja/web/pages/_app.js.jinja2 +14 -0
- reflex/.templates/web/utils/state.js +70 -41
- reflex/app.py +11 -11
- reflex/app_mixins/lifespan.py +24 -6
- reflex/app_module_for_backend.py +1 -1
- reflex/base.py +7 -13
- reflex/compiler/utils.py +17 -8
- reflex/components/base/bare.py +3 -1
- reflex/components/base/meta.py +5 -3
- reflex/components/component.py +28 -20
- reflex/components/core/breakpoints.py +1 -3
- reflex/components/core/cond.py +4 -4
- reflex/components/datadisplay/__init__.py +0 -1
- reflex/components/datadisplay/__init__.pyi +0 -1
- reflex/components/datadisplay/code.py +93 -106
- reflex/components/datadisplay/code.pyi +710 -53
- reflex/components/datadisplay/logo.py +22 -20
- reflex/components/dynamic.py +157 -0
- reflex/components/el/elements/forms.py +4 -1
- reflex/components/gridjs/datatable.py +2 -1
- reflex/components/markdown/markdown.py +10 -6
- reflex/components/markdown/markdown.pyi +3 -0
- reflex/components/radix/themes/components/progress.py +22 -0
- reflex/components/radix/themes/components/progress.pyi +2 -0
- reflex/components/radix/themes/components/segmented_control.py +3 -0
- reflex/components/radix/themes/components/segmented_control.pyi +2 -0
- reflex/components/radix/themes/layout/stack.py +1 -1
- reflex/components/recharts/cartesian.py +1 -1
- reflex/components/sonner/toast.py +3 -3
- reflex/components/tags/iter_tag.py +5 -1
- reflex/config.py +2 -2
- reflex/constants/base.py +4 -1
- reflex/constants/installer.py +8 -1
- reflex/event.py +63 -22
- reflex/experimental/assets.py +3 -1
- reflex/experimental/client_state.py +12 -7
- reflex/experimental/misc.py +5 -3
- reflex/middleware/hydrate_middleware.py +1 -2
- reflex/page.py +10 -3
- reflex/reflex.py +20 -3
- reflex/state.py +105 -44
- reflex/style.py +12 -2
- reflex/testing.py +8 -4
- reflex/utils/console.py +1 -1
- reflex/utils/exceptions.py +4 -0
- reflex/utils/exec.py +170 -18
- reflex/utils/format.py +6 -44
- reflex/utils/path_ops.py +36 -1
- reflex/utils/prerequisites.py +62 -21
- reflex/utils/serializers.py +7 -46
- reflex/utils/telemetry.py +1 -1
- reflex/utils/types.py +18 -3
- reflex/vars/base.py +303 -43
- reflex/vars/number.py +3 -0
- reflex/vars/sequence.py +43 -8
- {reflex-0.6.0a3.dist-info → reflex-0.6.1.dist-info}/METADATA +5 -5
- {reflex-0.6.0a3.dist-info → reflex-0.6.1.dist-info}/RECORD +61 -60
- {reflex-0.6.0a3.dist-info → reflex-0.6.1.dist-info}/LICENSE +0 -0
- {reflex-0.6.0a3.dist-info → reflex-0.6.1.dist-info}/WHEEL +0 -0
- {reflex-0.6.0a3.dist-info → reflex-0.6.1.dist-info}/entry_points.txt +0 -0
reflex/utils/serializers.py
CHANGED
|
@@ -12,7 +12,6 @@ from pathlib import Path
|
|
|
12
12
|
from typing import (
|
|
13
13
|
Any,
|
|
14
14
|
Callable,
|
|
15
|
-
Dict,
|
|
16
15
|
List,
|
|
17
16
|
Literal,
|
|
18
17
|
Optional,
|
|
@@ -126,7 +125,8 @@ def serialize(
|
|
|
126
125
|
# If there is no serializer, return None.
|
|
127
126
|
if serializer is None:
|
|
128
127
|
if dataclasses.is_dataclass(value) and not isinstance(value, type):
|
|
129
|
-
return
|
|
128
|
+
return {k.name: getattr(value, k.name) for k in dataclasses.fields(value)}
|
|
129
|
+
|
|
130
130
|
if get_type:
|
|
131
131
|
return None, None
|
|
132
132
|
return None
|
|
@@ -214,32 +214,6 @@ def serialize_type(value: type) -> str:
|
|
|
214
214
|
return value.__name__
|
|
215
215
|
|
|
216
216
|
|
|
217
|
-
@serializer
|
|
218
|
-
def serialize_str(value: str) -> str:
|
|
219
|
-
"""Serialize a string.
|
|
220
|
-
|
|
221
|
-
Args:
|
|
222
|
-
value: The string to serialize.
|
|
223
|
-
|
|
224
|
-
Returns:
|
|
225
|
-
The serialized string.
|
|
226
|
-
"""
|
|
227
|
-
return value
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
@serializer
|
|
231
|
-
def serialize_primitive(value: Union[bool, int, float, None]):
|
|
232
|
-
"""Serialize a primitive type.
|
|
233
|
-
|
|
234
|
-
Args:
|
|
235
|
-
value: The number/bool/None to serialize.
|
|
236
|
-
|
|
237
|
-
Returns:
|
|
238
|
-
The serialized number/bool/None.
|
|
239
|
-
"""
|
|
240
|
-
return value
|
|
241
|
-
|
|
242
|
-
|
|
243
217
|
@serializer
|
|
244
218
|
def serialize_base(value: Base) -> dict:
|
|
245
219
|
"""Serialize a Base instance.
|
|
@@ -250,33 +224,20 @@ def serialize_base(value: Base) -> dict:
|
|
|
250
224
|
Returns:
|
|
251
225
|
The serialized Base.
|
|
252
226
|
"""
|
|
253
|
-
return {k:
|
|
227
|
+
return {k: v for k, v in value.dict().items() if not callable(v)}
|
|
254
228
|
|
|
255
229
|
|
|
256
230
|
@serializer
|
|
257
|
-
def
|
|
258
|
-
"""Serialize a
|
|
231
|
+
def serialize_set(value: Set) -> list:
|
|
232
|
+
"""Serialize a set to a JSON serializable list.
|
|
259
233
|
|
|
260
234
|
Args:
|
|
261
|
-
value: The
|
|
235
|
+
value: The set to serialize.
|
|
262
236
|
|
|
263
237
|
Returns:
|
|
264
238
|
The serialized list.
|
|
265
239
|
"""
|
|
266
|
-
return
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
@serializer
|
|
270
|
-
def serialize_dict(prop: Dict[str, Any]) -> dict:
|
|
271
|
-
"""Serialize a dictionary to a JSON string.
|
|
272
|
-
|
|
273
|
-
Args:
|
|
274
|
-
prop: The dictionary to serialize.
|
|
275
|
-
|
|
276
|
-
Returns:
|
|
277
|
-
The serialized dictionary.
|
|
278
|
-
"""
|
|
279
|
-
return {k: serialize(v) for k, v in prop.items()}
|
|
240
|
+
return list(value)
|
|
280
241
|
|
|
281
242
|
|
|
282
243
|
@serializer(to=str)
|
reflex/utils/telemetry.py
CHANGED
reflex/utils/types.py
CHANGED
|
@@ -9,6 +9,7 @@ import sys
|
|
|
9
9
|
import types
|
|
10
10
|
from functools import cached_property, lru_cache, wraps
|
|
11
11
|
from typing import (
|
|
12
|
+
TYPE_CHECKING,
|
|
12
13
|
Any,
|
|
13
14
|
Callable,
|
|
14
15
|
ClassVar,
|
|
@@ -96,8 +97,22 @@ PrimitiveType = Union[int, float, bool, str, list, dict, set, tuple]
|
|
|
96
97
|
StateVar = Union[PrimitiveType, Base, None]
|
|
97
98
|
StateIterVar = Union[list, set, tuple]
|
|
98
99
|
|
|
99
|
-
|
|
100
|
-
|
|
100
|
+
if TYPE_CHECKING:
|
|
101
|
+
from reflex.vars.base import Var
|
|
102
|
+
|
|
103
|
+
# ArgsSpec = Callable[[Var], list[Var]]
|
|
104
|
+
ArgsSpec = (
|
|
105
|
+
Callable[[], List[Var]]
|
|
106
|
+
| Callable[[Var], List[Var]]
|
|
107
|
+
| Callable[[Var, Var], List[Var]]
|
|
108
|
+
| Callable[[Var, Var, Var], List[Var]]
|
|
109
|
+
| Callable[[Var, Var, Var, Var], List[Var]]
|
|
110
|
+
| Callable[[Var, Var, Var, Var, Var], List[Var]]
|
|
111
|
+
| Callable[[Var, Var, Var, Var, Var, Var], List[Var]]
|
|
112
|
+
| Callable[[Var, Var, Var, Var, Var, Var, Var], List[Var]]
|
|
113
|
+
)
|
|
114
|
+
else:
|
|
115
|
+
ArgsSpec = Callable[..., List[Any]]
|
|
101
116
|
|
|
102
117
|
|
|
103
118
|
PrimitiveToAnnotation = {
|
|
@@ -632,7 +647,7 @@ def validate_parameter_literals(func):
|
|
|
632
647
|
annotations = {param[0]: param[1].annotation for param in func_params}
|
|
633
648
|
|
|
634
649
|
# validate args
|
|
635
|
-
for param, arg in zip(annotations, args
|
|
650
|
+
for param, arg in zip(annotations, args):
|
|
636
651
|
if annotations[param] is inspect.Parameter.empty:
|
|
637
652
|
continue
|
|
638
653
|
validate_literal(param, arg, annotations[param], func.__name__)
|
reflex/vars/base.py
CHANGED
|
@@ -20,6 +20,7 @@ from typing import (
|
|
|
20
20
|
Any,
|
|
21
21
|
Callable,
|
|
22
22
|
Dict,
|
|
23
|
+
FrozenSet,
|
|
23
24
|
Generic,
|
|
24
25
|
Iterable,
|
|
25
26
|
List,
|
|
@@ -72,6 +73,7 @@ if TYPE_CHECKING:
|
|
|
72
73
|
|
|
73
74
|
|
|
74
75
|
VAR_TYPE = TypeVar("VAR_TYPE", covariant=True)
|
|
76
|
+
OTHER_VAR_TYPE = TypeVar("OTHER_VAR_TYPE")
|
|
75
77
|
|
|
76
78
|
warnings.filterwarnings("ignore", message="fields may not start with an underscore")
|
|
77
79
|
|
|
@@ -119,6 +121,17 @@ class Var(Generic[VAR_TYPE]):
|
|
|
119
121
|
"""
|
|
120
122
|
return self._js_expr
|
|
121
123
|
|
|
124
|
+
@property
|
|
125
|
+
def _var_field_name(self) -> str:
|
|
126
|
+
"""The name of the field.
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
The name of the field.
|
|
130
|
+
"""
|
|
131
|
+
var_data = self._get_all_var_data()
|
|
132
|
+
field_name = var_data.field_name if var_data else None
|
|
133
|
+
return field_name or self._js_expr
|
|
134
|
+
|
|
122
135
|
@property
|
|
123
136
|
@deprecated("Use `_js_expr` instead.")
|
|
124
137
|
def _var_name_unwrapped(self) -> str:
|
|
@@ -181,7 +194,19 @@ class Var(Generic[VAR_TYPE]):
|
|
|
181
194
|
and self._get_all_var_data() == other._get_all_var_data()
|
|
182
195
|
)
|
|
183
196
|
|
|
184
|
-
|
|
197
|
+
@overload
|
|
198
|
+
def _replace(
|
|
199
|
+
self, _var_type: Type[OTHER_VAR_TYPE], merge_var_data=None, **kwargs: Any
|
|
200
|
+
) -> Var[OTHER_VAR_TYPE]: ...
|
|
201
|
+
|
|
202
|
+
@overload
|
|
203
|
+
def _replace(
|
|
204
|
+
self, _var_type: GenericType | None = None, merge_var_data=None, **kwargs: Any
|
|
205
|
+
) -> Self: ...
|
|
206
|
+
|
|
207
|
+
def _replace(
|
|
208
|
+
self, _var_type: GenericType | None = None, merge_var_data=None, **kwargs: Any
|
|
209
|
+
) -> Self | Var:
|
|
185
210
|
"""Make a copy of this Var with updated fields.
|
|
186
211
|
|
|
187
212
|
Args:
|
|
@@ -205,14 +230,20 @@ class Var(Generic[VAR_TYPE]):
|
|
|
205
230
|
"The _var_full_name_needs_state_prefix argument is not supported for Var."
|
|
206
231
|
)
|
|
207
232
|
|
|
208
|
-
|
|
233
|
+
value_with_replaced = dataclasses.replace(
|
|
209
234
|
self,
|
|
235
|
+
_var_type=_var_type or self._var_type,
|
|
210
236
|
_var_data=VarData.merge(
|
|
211
237
|
kwargs.get("_var_data", self._var_data), merge_var_data
|
|
212
238
|
),
|
|
213
239
|
**kwargs,
|
|
214
240
|
)
|
|
215
241
|
|
|
242
|
+
if (js_expr := kwargs.get("_js_expr", None)) is not None:
|
|
243
|
+
object.__setattr__(value_with_replaced, "_js_expr", js_expr)
|
|
244
|
+
|
|
245
|
+
return value_with_replaced
|
|
246
|
+
|
|
216
247
|
@classmethod
|
|
217
248
|
def create(
|
|
218
249
|
cls,
|
|
@@ -566,8 +597,7 @@ class Var(Generic[VAR_TYPE]):
|
|
|
566
597
|
Returns:
|
|
567
598
|
The name of the setter function.
|
|
568
599
|
"""
|
|
569
|
-
|
|
570
|
-
setter = constants.SETTER_PREFIX + var_name_parts[-1]
|
|
600
|
+
setter = constants.SETTER_PREFIX + self._var_field_name
|
|
571
601
|
var_data = self._get_all_var_data()
|
|
572
602
|
if var_data is None:
|
|
573
603
|
return setter
|
|
@@ -581,7 +611,7 @@ class Var(Generic[VAR_TYPE]):
|
|
|
581
611
|
Returns:
|
|
582
612
|
A function that that creates a setter for the var.
|
|
583
613
|
"""
|
|
584
|
-
actual_name = self.
|
|
614
|
+
actual_name = self._var_field_name
|
|
585
615
|
|
|
586
616
|
def setter(state: BaseState, value: Any):
|
|
587
617
|
"""Get the setter for the var.
|
|
@@ -623,7 +653,9 @@ class Var(Generic[VAR_TYPE]):
|
|
|
623
653
|
return StateOperation.create(
|
|
624
654
|
formatted_state_name,
|
|
625
655
|
self,
|
|
626
|
-
_var_data=VarData.merge(
|
|
656
|
+
_var_data=VarData.merge(
|
|
657
|
+
VarData.from_state(state, self._js_expr), self._var_data
|
|
658
|
+
),
|
|
627
659
|
).guess_type()
|
|
628
660
|
|
|
629
661
|
def __eq__(self, other: Var | Any) -> BooleanVar:
|
|
@@ -1109,7 +1141,7 @@ def serialize_literal(value: LiteralVar):
|
|
|
1109
1141
|
Returns:
|
|
1110
1142
|
The serialized Literal.
|
|
1111
1143
|
"""
|
|
1112
|
-
return
|
|
1144
|
+
return value._var_value
|
|
1113
1145
|
|
|
1114
1146
|
|
|
1115
1147
|
P = ParamSpec("P")
|
|
@@ -1527,8 +1559,9 @@ class ComputedVar(Var[RETURN_TYPE]):
|
|
|
1527
1559
|
Raises:
|
|
1528
1560
|
TypeError: If the computed var dependencies are not Var instances or var names.
|
|
1529
1561
|
"""
|
|
1530
|
-
|
|
1531
|
-
|
|
1562
|
+
hint = kwargs.pop("return_type", None) or get_type_hints(fget).get(
|
|
1563
|
+
"return", Any
|
|
1564
|
+
)
|
|
1532
1565
|
|
|
1533
1566
|
kwargs["_js_expr"] = kwargs.pop("_js_expr", fget.__name__)
|
|
1534
1567
|
kwargs["_var_type"] = kwargs.pop("_var_type", hint)
|
|
@@ -1706,12 +1739,18 @@ class ComputedVar(Var[RETURN_TYPE]):
|
|
|
1706
1739
|
while self._js_expr in state_where_defined.inherited_vars:
|
|
1707
1740
|
state_where_defined = state_where_defined.get_parent_state()
|
|
1708
1741
|
|
|
1709
|
-
|
|
1710
|
-
|
|
1742
|
+
field_name = (
|
|
1743
|
+
format_state_name(state_where_defined.get_full_name())
|
|
1711
1744
|
+ "."
|
|
1712
|
-
+ self._js_expr
|
|
1713
|
-
|
|
1714
|
-
|
|
1745
|
+
+ self._js_expr
|
|
1746
|
+
)
|
|
1747
|
+
|
|
1748
|
+
return dispatch(
|
|
1749
|
+
field_name,
|
|
1750
|
+
var_data=VarData.from_state(state_where_defined, self._js_expr),
|
|
1751
|
+
result_var_type=self._var_type,
|
|
1752
|
+
existing_var=self,
|
|
1753
|
+
)
|
|
1715
1754
|
|
|
1716
1755
|
if not self._cache:
|
|
1717
1756
|
return self.fget(instance)
|
|
@@ -2339,6 +2378,9 @@ class VarData:
|
|
|
2339
2378
|
# The name of the enclosing state.
|
|
2340
2379
|
state: str = dataclasses.field(default="")
|
|
2341
2380
|
|
|
2381
|
+
# The name of the field in the state.
|
|
2382
|
+
field_name: str = dataclasses.field(default="")
|
|
2383
|
+
|
|
2342
2384
|
# Imports needed to render this var
|
|
2343
2385
|
imports: ImmutableParsedImportDict = dataclasses.field(default_factory=tuple)
|
|
2344
2386
|
|
|
@@ -2348,6 +2390,7 @@ class VarData:
|
|
|
2348
2390
|
def __init__(
|
|
2349
2391
|
self,
|
|
2350
2392
|
state: str = "",
|
|
2393
|
+
field_name: str = "",
|
|
2351
2394
|
imports: ImportDict | ParsedImportDict | None = None,
|
|
2352
2395
|
hooks: dict[str, None] | None = None,
|
|
2353
2396
|
):
|
|
@@ -2355,6 +2398,7 @@ class VarData:
|
|
|
2355
2398
|
|
|
2356
2399
|
Args:
|
|
2357
2400
|
state: The name of the enclosing state.
|
|
2401
|
+
field_name: The name of the field in the state.
|
|
2358
2402
|
imports: Imports needed to render this var.
|
|
2359
2403
|
hooks: Hooks that need to be present in the component to render this var.
|
|
2360
2404
|
"""
|
|
@@ -2364,6 +2408,7 @@ class VarData:
|
|
|
2364
2408
|
)
|
|
2365
2409
|
)
|
|
2366
2410
|
object.__setattr__(self, "state", state)
|
|
2411
|
+
object.__setattr__(self, "field_name", field_name)
|
|
2367
2412
|
object.__setattr__(self, "imports", immutable_imports)
|
|
2368
2413
|
object.__setattr__(self, "hooks", tuple(hooks or {}))
|
|
2369
2414
|
|
|
@@ -2386,12 +2431,14 @@ class VarData:
|
|
|
2386
2431
|
The merged var data object.
|
|
2387
2432
|
"""
|
|
2388
2433
|
state = ""
|
|
2434
|
+
field_name = ""
|
|
2389
2435
|
_imports = {}
|
|
2390
2436
|
hooks = {}
|
|
2391
2437
|
for var_data in others:
|
|
2392
2438
|
if var_data is None:
|
|
2393
2439
|
continue
|
|
2394
2440
|
state = state or var_data.state
|
|
2441
|
+
field_name = field_name or var_data.field_name
|
|
2395
2442
|
_imports = imports.merge_imports(_imports, var_data.imports)
|
|
2396
2443
|
hooks.update(
|
|
2397
2444
|
var_data.hooks
|
|
@@ -2399,9 +2446,10 @@ class VarData:
|
|
|
2399
2446
|
else {k: None for k in var_data.hooks}
|
|
2400
2447
|
)
|
|
2401
2448
|
|
|
2402
|
-
if state or _imports or hooks:
|
|
2449
|
+
if state or _imports or hooks or field_name:
|
|
2403
2450
|
return VarData(
|
|
2404
2451
|
state=state,
|
|
2452
|
+
field_name=field_name,
|
|
2405
2453
|
imports=_imports,
|
|
2406
2454
|
hooks=hooks,
|
|
2407
2455
|
)
|
|
@@ -2413,38 +2461,15 @@ class VarData:
|
|
|
2413
2461
|
Returns:
|
|
2414
2462
|
True if any field is set to a non-default value.
|
|
2415
2463
|
"""
|
|
2416
|
-
return bool(self.state or self.imports or self.hooks)
|
|
2417
|
-
|
|
2418
|
-
def __eq__(self, other: Any) -> bool:
|
|
2419
|
-
"""Check if two var data objects are equal.
|
|
2420
|
-
|
|
2421
|
-
Args:
|
|
2422
|
-
other: The other var data object to compare.
|
|
2423
|
-
|
|
2424
|
-
Returns:
|
|
2425
|
-
True if all fields are equal and collapsed imports are equal.
|
|
2426
|
-
"""
|
|
2427
|
-
if not isinstance(other, VarData):
|
|
2428
|
-
return False
|
|
2429
|
-
|
|
2430
|
-
# Don't compare interpolations - that's added in by the decoder, and
|
|
2431
|
-
# not part of the vardata itself.
|
|
2432
|
-
return (
|
|
2433
|
-
self.state == other.state
|
|
2434
|
-
and self.hooks
|
|
2435
|
-
== (
|
|
2436
|
-
other.hooks if isinstance(other, VarData) else tuple(other.hooks.keys())
|
|
2437
|
-
)
|
|
2438
|
-
and imports.collapse_imports(self.imports)
|
|
2439
|
-
== imports.collapse_imports(other.imports)
|
|
2440
|
-
)
|
|
2464
|
+
return bool(self.state or self.imports or self.hooks or self.field_name)
|
|
2441
2465
|
|
|
2442
2466
|
@classmethod
|
|
2443
|
-
def from_state(cls, state: Type[BaseState] | str) -> VarData:
|
|
2467
|
+
def from_state(cls, state: Type[BaseState] | str, field_name: str = "") -> VarData:
|
|
2444
2468
|
"""Set the state of the var.
|
|
2445
2469
|
|
|
2446
2470
|
Args:
|
|
2447
2471
|
state: The state to set or the full name of the state.
|
|
2472
|
+
field_name: The name of the field in the state. Optional.
|
|
2448
2473
|
|
|
2449
2474
|
Returns:
|
|
2450
2475
|
The var with the set state.
|
|
@@ -2452,8 +2477,9 @@ class VarData:
|
|
|
2452
2477
|
from reflex.utils import format
|
|
2453
2478
|
|
|
2454
2479
|
state_name = state if isinstance(state, str) else state.get_full_name()
|
|
2455
|
-
|
|
2480
|
+
return VarData(
|
|
2456
2481
|
state=state_name,
|
|
2482
|
+
field_name=field_name,
|
|
2457
2483
|
hooks={
|
|
2458
2484
|
"const {0} = useContext(StateContexts.{0})".format(
|
|
2459
2485
|
format.format_state_name(state_name)
|
|
@@ -2464,7 +2490,6 @@ class VarData:
|
|
|
2464
2490
|
"react": [ImportVar(tag="useContext")],
|
|
2465
2491
|
},
|
|
2466
2492
|
)
|
|
2467
|
-
return new_var_data
|
|
2468
2493
|
|
|
2469
2494
|
|
|
2470
2495
|
def _decode_var_immutable(value: str) -> tuple[VarData | None, str]:
|
|
@@ -2561,3 +2586,238 @@ REPLACED_NAMES = {
|
|
|
2561
2586
|
"set_state": "_var_set_state",
|
|
2562
2587
|
"deps": "_deps",
|
|
2563
2588
|
}
|
|
2589
|
+
|
|
2590
|
+
|
|
2591
|
+
dispatchers: Dict[GenericType, Callable[[Var], Var]] = {}
|
|
2592
|
+
|
|
2593
|
+
|
|
2594
|
+
def transform(fn: Callable[[Var], Var]) -> Callable[[Var], Var]:
|
|
2595
|
+
"""Register a function to transform a Var.
|
|
2596
|
+
|
|
2597
|
+
Args:
|
|
2598
|
+
fn: The function to register.
|
|
2599
|
+
|
|
2600
|
+
Returns:
|
|
2601
|
+
The decorator.
|
|
2602
|
+
|
|
2603
|
+
Raises:
|
|
2604
|
+
TypeError: If the return type of the function is not a Var.
|
|
2605
|
+
TypeError: If the Var return type does not have a generic type.
|
|
2606
|
+
ValueError: If a function for the generic type is already registered.
|
|
2607
|
+
"""
|
|
2608
|
+
return_type = fn.__annotations__["return"]
|
|
2609
|
+
|
|
2610
|
+
origin = get_origin(return_type)
|
|
2611
|
+
|
|
2612
|
+
if origin is not Var:
|
|
2613
|
+
raise TypeError(
|
|
2614
|
+
f"Expected return type of {fn.__name__} to be a Var, got {origin}."
|
|
2615
|
+
)
|
|
2616
|
+
|
|
2617
|
+
generic_args = get_args(return_type)
|
|
2618
|
+
|
|
2619
|
+
if not generic_args:
|
|
2620
|
+
raise TypeError(
|
|
2621
|
+
f"Expected Var return type of {fn.__name__} to have a generic type."
|
|
2622
|
+
)
|
|
2623
|
+
|
|
2624
|
+
generic_type = get_origin(generic_args[0]) or generic_args[0]
|
|
2625
|
+
|
|
2626
|
+
if generic_type in dispatchers:
|
|
2627
|
+
raise ValueError(f"Function for {generic_type} already registered.")
|
|
2628
|
+
|
|
2629
|
+
dispatchers[generic_type] = fn
|
|
2630
|
+
|
|
2631
|
+
return fn
|
|
2632
|
+
|
|
2633
|
+
|
|
2634
|
+
def generic_type_to_actual_type_map(
|
|
2635
|
+
generic_type: GenericType, actual_type: GenericType
|
|
2636
|
+
) -> Dict[TypeVar, GenericType]:
|
|
2637
|
+
"""Map the generic type to the actual type.
|
|
2638
|
+
|
|
2639
|
+
Args:
|
|
2640
|
+
generic_type: The generic type.
|
|
2641
|
+
actual_type: The actual type.
|
|
2642
|
+
|
|
2643
|
+
Returns:
|
|
2644
|
+
The mapping of type variables to actual types.
|
|
2645
|
+
|
|
2646
|
+
Raises:
|
|
2647
|
+
TypeError: If the generic type and actual type do not match.
|
|
2648
|
+
TypeError: If the number of generic arguments and actual arguments do not match.
|
|
2649
|
+
"""
|
|
2650
|
+
generic_origin = get_origin(generic_type) or generic_type
|
|
2651
|
+
actual_origin = get_origin(actual_type) or actual_type
|
|
2652
|
+
|
|
2653
|
+
if generic_origin is not actual_origin:
|
|
2654
|
+
if isinstance(generic_origin, TypeVar):
|
|
2655
|
+
return {generic_origin: actual_origin}
|
|
2656
|
+
raise TypeError(
|
|
2657
|
+
f"Type mismatch: expected {generic_origin}, got {actual_origin}."
|
|
2658
|
+
)
|
|
2659
|
+
|
|
2660
|
+
generic_args = get_args(generic_type)
|
|
2661
|
+
actual_args = get_args(actual_type)
|
|
2662
|
+
|
|
2663
|
+
if len(generic_args) != len(actual_args):
|
|
2664
|
+
raise TypeError(
|
|
2665
|
+
f"Number of generic arguments mismatch: expected {len(generic_args)}, got {len(actual_args)}."
|
|
2666
|
+
)
|
|
2667
|
+
|
|
2668
|
+
# call recursively for nested generic types and merge the results
|
|
2669
|
+
return {
|
|
2670
|
+
k: v
|
|
2671
|
+
for generic_arg, actual_arg in zip(generic_args, actual_args)
|
|
2672
|
+
for k, v in generic_type_to_actual_type_map(generic_arg, actual_arg).items()
|
|
2673
|
+
}
|
|
2674
|
+
|
|
2675
|
+
|
|
2676
|
+
def resolve_generic_type_with_mapping(
|
|
2677
|
+
generic_type: GenericType, type_mapping: Dict[TypeVar, GenericType]
|
|
2678
|
+
):
|
|
2679
|
+
"""Resolve a generic type with a type mapping.
|
|
2680
|
+
|
|
2681
|
+
Args:
|
|
2682
|
+
generic_type: The generic type.
|
|
2683
|
+
type_mapping: The type mapping.
|
|
2684
|
+
|
|
2685
|
+
Returns:
|
|
2686
|
+
The resolved generic type.
|
|
2687
|
+
"""
|
|
2688
|
+
if isinstance(generic_type, TypeVar):
|
|
2689
|
+
return type_mapping.get(generic_type, generic_type)
|
|
2690
|
+
|
|
2691
|
+
generic_origin = get_origin(generic_type) or generic_type
|
|
2692
|
+
|
|
2693
|
+
generic_args = get_args(generic_type)
|
|
2694
|
+
|
|
2695
|
+
if not generic_args:
|
|
2696
|
+
return generic_type
|
|
2697
|
+
|
|
2698
|
+
mapping_for_older_python = {
|
|
2699
|
+
list: List,
|
|
2700
|
+
set: Set,
|
|
2701
|
+
dict: Dict,
|
|
2702
|
+
tuple: Tuple,
|
|
2703
|
+
frozenset: FrozenSet,
|
|
2704
|
+
}
|
|
2705
|
+
|
|
2706
|
+
return mapping_for_older_python.get(generic_origin, generic_origin)[
|
|
2707
|
+
tuple(
|
|
2708
|
+
resolve_generic_type_with_mapping(arg, type_mapping) for arg in generic_args
|
|
2709
|
+
)
|
|
2710
|
+
]
|
|
2711
|
+
|
|
2712
|
+
|
|
2713
|
+
def resolve_arg_type_from_return_type(
|
|
2714
|
+
arg_type: GenericType, return_type: GenericType, actual_return_type: GenericType
|
|
2715
|
+
) -> GenericType:
|
|
2716
|
+
"""Resolve the argument type from the return type.
|
|
2717
|
+
|
|
2718
|
+
Args:
|
|
2719
|
+
arg_type: The argument type.
|
|
2720
|
+
return_type: The return type.
|
|
2721
|
+
actual_return_type: The requested return type.
|
|
2722
|
+
|
|
2723
|
+
Returns:
|
|
2724
|
+
The argument type without the generics that are resolved.
|
|
2725
|
+
"""
|
|
2726
|
+
return resolve_generic_type_with_mapping(
|
|
2727
|
+
arg_type, generic_type_to_actual_type_map(return_type, actual_return_type)
|
|
2728
|
+
)
|
|
2729
|
+
|
|
2730
|
+
|
|
2731
|
+
def dispatch(
|
|
2732
|
+
field_name: str,
|
|
2733
|
+
var_data: VarData,
|
|
2734
|
+
result_var_type: GenericType,
|
|
2735
|
+
existing_var: Var | None = None,
|
|
2736
|
+
) -> Var:
|
|
2737
|
+
"""Dispatch a Var to the appropriate transformation function.
|
|
2738
|
+
|
|
2739
|
+
Args:
|
|
2740
|
+
field_name: The name of the field.
|
|
2741
|
+
var_data: The VarData associated with the Var.
|
|
2742
|
+
result_var_type: The type of the Var.
|
|
2743
|
+
existing_var: The existing Var to transform. Optional.
|
|
2744
|
+
|
|
2745
|
+
Returns:
|
|
2746
|
+
The transformed Var.
|
|
2747
|
+
|
|
2748
|
+
Raises:
|
|
2749
|
+
TypeError: If the return type of the function is not a Var.
|
|
2750
|
+
TypeError: If the Var return type does not have a generic type.
|
|
2751
|
+
TypeError: If the first argument of the function is not a Var.
|
|
2752
|
+
TypeError: If the first argument of the function does not have a generic type
|
|
2753
|
+
"""
|
|
2754
|
+
result_origin_var_type = get_origin(result_var_type) or result_var_type
|
|
2755
|
+
|
|
2756
|
+
if result_origin_var_type in dispatchers:
|
|
2757
|
+
fn = dispatchers[result_origin_var_type]
|
|
2758
|
+
fn_first_arg_type = list(inspect.signature(fn).parameters.values())[
|
|
2759
|
+
0
|
|
2760
|
+
].annotation
|
|
2761
|
+
|
|
2762
|
+
fn_return = inspect.signature(fn).return_annotation
|
|
2763
|
+
|
|
2764
|
+
fn_return_origin = get_origin(fn_return) or fn_return
|
|
2765
|
+
|
|
2766
|
+
if fn_return_origin is not Var:
|
|
2767
|
+
raise TypeError(
|
|
2768
|
+
f"Expected return type of {fn.__name__} to be a Var, got {fn_return}."
|
|
2769
|
+
)
|
|
2770
|
+
|
|
2771
|
+
fn_return_generic_args = get_args(fn_return)
|
|
2772
|
+
|
|
2773
|
+
if not fn_return_generic_args:
|
|
2774
|
+
raise TypeError(f"Expected generic type of {fn_return} to be a type.")
|
|
2775
|
+
|
|
2776
|
+
arg_origin = get_origin(fn_first_arg_type) or fn_first_arg_type
|
|
2777
|
+
|
|
2778
|
+
if arg_origin is not Var:
|
|
2779
|
+
raise TypeError(
|
|
2780
|
+
f"Expected first argument of {fn.__name__} to be a Var, got {fn_first_arg_type}."
|
|
2781
|
+
)
|
|
2782
|
+
|
|
2783
|
+
arg_generic_args = get_args(fn_first_arg_type)
|
|
2784
|
+
|
|
2785
|
+
if not arg_generic_args:
|
|
2786
|
+
raise TypeError(
|
|
2787
|
+
f"Expected generic type of {fn_first_arg_type} to be a type."
|
|
2788
|
+
)
|
|
2789
|
+
|
|
2790
|
+
arg_type = arg_generic_args[0]
|
|
2791
|
+
fn_return_type = fn_return_generic_args[0]
|
|
2792
|
+
|
|
2793
|
+
var = (
|
|
2794
|
+
Var(
|
|
2795
|
+
field_name,
|
|
2796
|
+
_var_data=var_data,
|
|
2797
|
+
_var_type=resolve_arg_type_from_return_type(
|
|
2798
|
+
arg_type, fn_return_type, result_var_type
|
|
2799
|
+
),
|
|
2800
|
+
).guess_type()
|
|
2801
|
+
if existing_var is None
|
|
2802
|
+
else existing_var._replace(
|
|
2803
|
+
_var_type=resolve_arg_type_from_return_type(
|
|
2804
|
+
arg_type, fn_return_type, result_var_type
|
|
2805
|
+
),
|
|
2806
|
+
_var_data=var_data,
|
|
2807
|
+
_js_expr=field_name,
|
|
2808
|
+
).guess_type()
|
|
2809
|
+
)
|
|
2810
|
+
|
|
2811
|
+
return fn(var)
|
|
2812
|
+
|
|
2813
|
+
if existing_var is not None:
|
|
2814
|
+
return existing_var._replace(
|
|
2815
|
+
_js_expr=field_name,
|
|
2816
|
+
_var_data=var_data,
|
|
2817
|
+
_var_type=result_var_type,
|
|
2818
|
+
).guess_type()
|
|
2819
|
+
return Var(
|
|
2820
|
+
field_name,
|
|
2821
|
+
_var_data=var_data,
|
|
2822
|
+
_var_type=result_var_type,
|
|
2823
|
+
).guess_type()
|
reflex/vars/number.py
CHANGED
|
@@ -21,6 +21,7 @@ from typing import (
|
|
|
21
21
|
from reflex.constants.base import Dirs
|
|
22
22
|
from reflex.utils.exceptions import PrimitiveUnserializableToJSON, VarTypeError
|
|
23
23
|
from reflex.utils.imports import ImportDict, ImportVar
|
|
24
|
+
from reflex.utils.types import is_optional
|
|
24
25
|
|
|
25
26
|
from .base import (
|
|
26
27
|
CustomVarOperationReturn,
|
|
@@ -524,6 +525,8 @@ class NumberVar(Var[NUMBER_T]):
|
|
|
524
525
|
Returns:
|
|
525
526
|
The boolean value of the number.
|
|
526
527
|
"""
|
|
528
|
+
if is_optional(self._var_type):
|
|
529
|
+
return boolify((self != None) & (self != 0)) # noqa: E711
|
|
527
530
|
return self != 0
|
|
528
531
|
|
|
529
532
|
def _is_strict_float(self) -> bool:
|