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.

Files changed (61) hide show
  1. reflex/.templates/jinja/custom_components/pyproject.toml.jinja2 +1 -1
  2. reflex/.templates/jinja/web/pages/_app.js.jinja2 +14 -0
  3. reflex/.templates/web/utils/state.js +70 -41
  4. reflex/app.py +11 -11
  5. reflex/app_mixins/lifespan.py +24 -6
  6. reflex/app_module_for_backend.py +1 -1
  7. reflex/base.py +7 -13
  8. reflex/compiler/utils.py +17 -8
  9. reflex/components/base/bare.py +3 -1
  10. reflex/components/base/meta.py +5 -3
  11. reflex/components/component.py +28 -20
  12. reflex/components/core/breakpoints.py +1 -3
  13. reflex/components/core/cond.py +4 -4
  14. reflex/components/datadisplay/__init__.py +0 -1
  15. reflex/components/datadisplay/__init__.pyi +0 -1
  16. reflex/components/datadisplay/code.py +93 -106
  17. reflex/components/datadisplay/code.pyi +710 -53
  18. reflex/components/datadisplay/logo.py +22 -20
  19. reflex/components/dynamic.py +157 -0
  20. reflex/components/el/elements/forms.py +4 -1
  21. reflex/components/gridjs/datatable.py +2 -1
  22. reflex/components/markdown/markdown.py +10 -6
  23. reflex/components/markdown/markdown.pyi +3 -0
  24. reflex/components/radix/themes/components/progress.py +22 -0
  25. reflex/components/radix/themes/components/progress.pyi +2 -0
  26. reflex/components/radix/themes/components/segmented_control.py +3 -0
  27. reflex/components/radix/themes/components/segmented_control.pyi +2 -0
  28. reflex/components/radix/themes/layout/stack.py +1 -1
  29. reflex/components/recharts/cartesian.py +1 -1
  30. reflex/components/sonner/toast.py +3 -3
  31. reflex/components/tags/iter_tag.py +5 -1
  32. reflex/config.py +2 -2
  33. reflex/constants/base.py +4 -1
  34. reflex/constants/installer.py +8 -1
  35. reflex/event.py +63 -22
  36. reflex/experimental/assets.py +3 -1
  37. reflex/experimental/client_state.py +12 -7
  38. reflex/experimental/misc.py +5 -3
  39. reflex/middleware/hydrate_middleware.py +1 -2
  40. reflex/page.py +10 -3
  41. reflex/reflex.py +20 -3
  42. reflex/state.py +105 -44
  43. reflex/style.py +12 -2
  44. reflex/testing.py +8 -4
  45. reflex/utils/console.py +1 -1
  46. reflex/utils/exceptions.py +4 -0
  47. reflex/utils/exec.py +170 -18
  48. reflex/utils/format.py +6 -44
  49. reflex/utils/path_ops.py +36 -1
  50. reflex/utils/prerequisites.py +62 -21
  51. reflex/utils/serializers.py +7 -46
  52. reflex/utils/telemetry.py +1 -1
  53. reflex/utils/types.py +18 -3
  54. reflex/vars/base.py +303 -43
  55. reflex/vars/number.py +3 -0
  56. reflex/vars/sequence.py +43 -8
  57. {reflex-0.6.0a3.dist-info → reflex-0.6.1.dist-info}/METADATA +5 -5
  58. {reflex-0.6.0a3.dist-info → reflex-0.6.1.dist-info}/RECORD +61 -60
  59. {reflex-0.6.0a3.dist-info → reflex-0.6.1.dist-info}/LICENSE +0 -0
  60. {reflex-0.6.0a3.dist-info → reflex-0.6.1.dist-info}/WHEEL +0 -0
  61. {reflex-0.6.0a3.dist-info → reflex-0.6.1.dist-info}/entry_points.txt +0 -0
@@ -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 serialize(dataclasses.asdict(value))
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: serialize(v) for k, v in value.dict().items() if not callable(v)}
227
+ return {k: v for k, v in value.dict().items() if not callable(v)}
254
228
 
255
229
 
256
230
  @serializer
257
- def serialize_list(value: Union[List, Tuple, Set]) -> list:
258
- """Serialize a list to a JSON string.
231
+ def serialize_set(value: Set) -> list:
232
+ """Serialize a set to a JSON serializable list.
259
233
 
260
234
  Args:
261
- value: The list to serialize.
235
+ value: The set to serialize.
262
236
 
263
237
  Returns:
264
238
  The serialized list.
265
239
  """
266
- return [serialize(item) for item in value]
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
@@ -121,7 +121,7 @@ def _prepare_event(event: str, **kwargs) -> dict:
121
121
  return {}
122
122
 
123
123
  if UTC is None:
124
- # for python 3.10
124
+ # for python 3.9 & 3.10
125
125
  stamp = datetime.utcnow().isoformat()
126
126
  else:
127
127
  # for python 3.11 & 3.12
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
- # ArgsSpec = Callable[[Var], list[Var]]
100
- ArgsSpec = Callable
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, strict=False):
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
- def _replace(self, merge_var_data=None, **kwargs: Any):
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
- return dataclasses.replace(
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
- var_name_parts = self._js_expr.split(".")
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._js_expr.split(".")[-1]
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(VarData.from_state(state), self._var_data),
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 serializers.serialize(value._var_value)
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
- hints = get_type_hints(fget)
1531
- hint = hints.get("return", Any)
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
- return self._replace(
1710
- _js_expr=format_state_name(state_where_defined.get_full_name())
1742
+ field_name = (
1743
+ format_state_name(state_where_defined.get_full_name())
1711
1744
  + "."
1712
- + self._js_expr,
1713
- merge_var_data=VarData.from_state(state_where_defined),
1714
- ).guess_type()
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
- new_var_data = VarData(
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: