reflex 0.7.4a3__py3-none-any.whl → 0.7.5a1__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 (160) hide show
  1. reflex/__init__.py +1 -0
  2. reflex/__init__.pyi +1 -0
  3. reflex/app.py +10 -6
  4. reflex/app_mixins/middleware.py +13 -20
  5. reflex/compiler/compiler.py +10 -3
  6. reflex/compiler/utils.py +4 -4
  7. reflex/components/base/app_wrap.pyi +7 -3
  8. reflex/components/base/body.pyi +7 -3
  9. reflex/components/base/document.pyi +27 -7
  10. reflex/components/base/error_boundary.pyi +7 -3
  11. reflex/components/base/fragment.pyi +7 -3
  12. reflex/components/base/head.pyi +12 -4
  13. reflex/components/base/link.pyi +12 -4
  14. reflex/components/base/meta.pyi +22 -6
  15. reflex/components/base/script.pyi +7 -3
  16. reflex/components/base/strict_mode.pyi +7 -3
  17. reflex/components/component.py +64 -23
  18. reflex/components/core/auto_scroll.pyi +7 -3
  19. reflex/components/core/banner.py +6 -2
  20. reflex/components/core/banner.pyi +32 -8
  21. reflex/components/core/client_side_routing.pyi +12 -4
  22. reflex/components/core/clipboard.pyi +7 -3
  23. reflex/components/core/debounce.pyi +7 -3
  24. reflex/components/core/foreach.py +5 -1
  25. reflex/components/core/html.pyi +7 -3
  26. reflex/components/core/match.py +5 -5
  27. reflex/components/core/sticky.pyi +21 -6
  28. reflex/components/core/upload.pyi +27 -7
  29. reflex/components/datadisplay/code.pyi +12 -4
  30. reflex/components/datadisplay/dataeditor.py +2 -2
  31. reflex/components/datadisplay/dataeditor.pyi +17 -3
  32. reflex/components/datadisplay/shiki_code_block.pyi +17 -4
  33. reflex/components/el/__init__.pyi +1 -1
  34. reflex/components/el/element.pyi +7 -3
  35. reflex/components/el/elements/__init__.py +3 -1
  36. reflex/components/el/elements/__init__.pyi +3 -2
  37. reflex/components/el/elements/base.pyi +7 -3
  38. reflex/components/el/elements/forms.py +1 -1
  39. reflex/components/el/elements/forms.pyi +72 -16
  40. reflex/components/el/elements/inline.pyi +142 -30
  41. reflex/components/el/elements/media.pyi +127 -27
  42. reflex/components/el/elements/metadata.pyi +32 -8
  43. reflex/components/el/elements/other.pyi +37 -9
  44. reflex/components/el/elements/scripts.pyi +17 -5
  45. reflex/components/el/elements/sectioning.pyi +77 -17
  46. reflex/components/el/elements/tables.pyi +52 -12
  47. reflex/components/el/elements/typography.pyi +77 -17
  48. reflex/components/gridjs/datatable.py +2 -2
  49. reflex/components/gridjs/datatable.pyi +12 -4
  50. reflex/components/lucide/icon.py +7 -6
  51. reflex/components/lucide/icon.pyi +22 -7
  52. reflex/components/markdown/markdown.py +1 -1
  53. reflex/components/markdown/markdown.pyi +7 -3
  54. reflex/components/moment/moment.pyi +7 -3
  55. reflex/components/next/base.pyi +7 -3
  56. reflex/components/next/image.pyi +7 -3
  57. reflex/components/next/link.pyi +7 -3
  58. reflex/components/next/video.pyi +7 -3
  59. reflex/components/plotly/plotly.pyi +47 -11
  60. reflex/components/radix/primitives/accordion.pyi +37 -9
  61. reflex/components/radix/primitives/base.pyi +12 -4
  62. reflex/components/radix/primitives/drawer.pyi +57 -13
  63. reflex/components/radix/primitives/form.pyi +52 -12
  64. reflex/components/radix/primitives/progress.pyi +27 -7
  65. reflex/components/radix/primitives/slider.pyi +27 -7
  66. reflex/components/radix/themes/base.pyi +41 -10
  67. reflex/components/radix/themes/color_mode.py +2 -2
  68. reflex/components/radix/themes/color_mode.pyi +17 -5
  69. reflex/components/radix/themes/components/alert_dialog.pyi +36 -9
  70. reflex/components/radix/themes/components/aspect_ratio.pyi +7 -3
  71. reflex/components/radix/themes/components/avatar.pyi +6 -3
  72. reflex/components/radix/themes/components/badge.pyi +6 -3
  73. reflex/components/radix/themes/components/button.pyi +6 -3
  74. reflex/components/radix/themes/components/callout.pyi +26 -7
  75. reflex/components/radix/themes/components/card.pyi +6 -3
  76. reflex/components/radix/themes/components/checkbox.pyi +16 -5
  77. reflex/components/radix/themes/components/checkbox_cards.pyi +11 -4
  78. reflex/components/radix/themes/components/checkbox_group.pyi +11 -4
  79. reflex/components/radix/themes/components/context_menu.pyi +66 -15
  80. reflex/components/radix/themes/components/data_list.pyi +21 -6
  81. reflex/components/radix/themes/components/dialog.pyi +36 -9
  82. reflex/components/radix/themes/components/dropdown_menu.pyi +41 -10
  83. reflex/components/radix/themes/components/hover_card.pyi +21 -6
  84. reflex/components/radix/themes/components/icon_button.pyi +6 -3
  85. reflex/components/radix/themes/components/inset.pyi +6 -3
  86. reflex/components/radix/themes/components/popover.pyi +21 -6
  87. reflex/components/radix/themes/components/progress.pyi +6 -3
  88. reflex/components/radix/themes/components/radio.pyi +6 -3
  89. reflex/components/radix/themes/components/radio_cards.pyi +11 -4
  90. reflex/components/radix/themes/components/radio_group.py +6 -1
  91. reflex/components/radix/themes/components/radio_group.pyi +21 -6
  92. reflex/components/radix/themes/components/scroll_area.pyi +7 -3
  93. reflex/components/radix/themes/components/segmented_control.pyi +11 -4
  94. reflex/components/radix/themes/components/select.pyi +46 -11
  95. reflex/components/radix/themes/components/separator.pyi +6 -3
  96. reflex/components/radix/themes/components/skeleton.pyi +6 -3
  97. reflex/components/radix/themes/components/slider.pyi +6 -3
  98. reflex/components/radix/themes/components/spinner.pyi +6 -3
  99. reflex/components/radix/themes/components/switch.pyi +6 -3
  100. reflex/components/radix/themes/components/table.pyi +36 -9
  101. reflex/components/radix/themes/components/tabs.pyi +26 -7
  102. reflex/components/radix/themes/components/text_area.pyi +6 -3
  103. reflex/components/radix/themes/components/text_field.py +3 -2
  104. reflex/components/radix/themes/components/text_field.pyi +16 -5
  105. reflex/components/radix/themes/components/tooltip.pyi +7 -3
  106. reflex/components/radix/themes/layout/base.pyi +6 -3
  107. reflex/components/radix/themes/layout/box.pyi +7 -3
  108. reflex/components/radix/themes/layout/center.pyi +6 -3
  109. reflex/components/radix/themes/layout/container.pyi +6 -3
  110. reflex/components/radix/themes/layout/flex.pyi +6 -3
  111. reflex/components/radix/themes/layout/grid.pyi +6 -3
  112. reflex/components/radix/themes/layout/list.pyi +27 -7
  113. reflex/components/radix/themes/layout/section.pyi +6 -3
  114. reflex/components/radix/themes/layout/spacer.pyi +6 -3
  115. reflex/components/radix/themes/layout/stack.pyi +16 -5
  116. reflex/components/radix/themes/typography/blockquote.pyi +6 -3
  117. reflex/components/radix/themes/typography/code.pyi +6 -3
  118. reflex/components/radix/themes/typography/heading.pyi +6 -3
  119. reflex/components/radix/themes/typography/link.pyi +6 -3
  120. reflex/components/radix/themes/typography/text.pyi +36 -9
  121. reflex/components/react_player/audio.pyi +7 -3
  122. reflex/components/react_player/react_player.pyi +7 -3
  123. reflex/components/react_player/video.pyi +7 -3
  124. reflex/components/recharts/cartesian.pyi +97 -21
  125. reflex/components/recharts/charts.pyi +62 -14
  126. reflex/components/recharts/general.pyi +32 -8
  127. reflex/components/recharts/polar.py +1 -1
  128. reflex/components/recharts/polar.pyi +33 -9
  129. reflex/components/recharts/recharts.pyi +12 -4
  130. reflex/components/sonner/toast.pyi +7 -2
  131. reflex/components/suneditor/editor.pyi +7 -3
  132. reflex/config.py +15 -1
  133. reflex/constants/installer.py +22 -1
  134. reflex/custom_components/custom_components.py +12 -7
  135. reflex/event.py +26 -10
  136. reflex/experimental/__init__.py +17 -6
  137. reflex/experimental/layout.pyi +27 -7
  138. reflex/model.py +3 -3
  139. reflex/reflex.py +33 -18
  140. reflex/state.py +3 -3
  141. reflex/style.py +2 -2
  142. reflex/testing.py +17 -5
  143. reflex/utils/console.py +2 -3
  144. reflex/utils/exec.py +4 -0
  145. reflex/utils/imports.py +14 -7
  146. reflex/utils/prerequisites.py +72 -7
  147. reflex/utils/processes.py +52 -19
  148. reflex/utils/pyi_generator.py +66 -53
  149. reflex/utils/registry.py +5 -3
  150. reflex/utils/serializers.py +1 -2
  151. reflex/utils/types.py +4 -4
  152. reflex/vars/base.py +58 -22
  153. reflex/vars/number.py +23 -6
  154. reflex/vars/sequence.py +2 -0
  155. {reflex-0.7.4a3.dist-info → reflex-0.7.5a1.dist-info}/METADATA +2 -2
  156. {reflex-0.7.4a3.dist-info → reflex-0.7.5a1.dist-info}/RECORD +160 -160
  157. /reflex/{experimental → utils}/misc.py +0 -0
  158. {reflex-0.7.4a3.dist-info → reflex-0.7.5a1.dist-info}/WHEEL +0 -0
  159. {reflex-0.7.4a3.dist-info → reflex-0.7.5a1.dist-info}/entry_points.txt +0 -0
  160. {reflex-0.7.4a3.dist-info → reflex-0.7.5a1.dist-info}/licenses/LICENSE +0 -0
@@ -55,6 +55,10 @@ EXCLUDED_PROPS = [
55
55
  "State",
56
56
  ]
57
57
 
58
+ OVERWRITE_TYPES = {
59
+ "style": "Sequence[Mapping[str, Any]] | Mapping[str, Any] | Var[Mapping[str, Any]] | Breakpoints | None",
60
+ }
61
+
58
62
  DEFAULT_TYPING_IMPORTS = {
59
63
  "overload",
60
64
  "Any",
@@ -62,6 +66,7 @@ DEFAULT_TYPING_IMPORTS = {
62
66
  "Dict",
63
67
  # "List",
64
68
  "Sequence",
69
+ "Mapping",
65
70
  "Literal",
66
71
  "Optional",
67
72
  "Union",
@@ -377,7 +382,9 @@ def _extract_class_props_as_ast_nodes(
377
382
  ast.arg(
378
383
  arg=name,
379
384
  annotation=ast.Name(
380
- id=_get_type_hint(value, type_hint_globals)
385
+ id=OVERWRITE_TYPES.get(
386
+ name, _get_type_hint(value, type_hint_globals)
387
+ )
381
388
  ),
382
389
  ),
383
390
  ast.Constant(value=default),
@@ -386,7 +393,7 @@ def _extract_class_props_as_ast_nodes(
386
393
  return kwargs
387
394
 
388
395
 
389
- def type_to_ast(typ: Any, cls: type) -> ast.AST:
396
+ def type_to_ast(typ: Any, cls: type) -> ast.expr:
390
397
  """Converts any type annotation into its AST representation.
391
398
  Handles nested generic types, unions, etc.
392
399
 
@@ -439,11 +446,11 @@ def type_to_ast(typ: Any, cls: type) -> ast.AST:
439
446
  if len(arg_nodes) == 1:
440
447
  slice_value = arg_nodes[0]
441
448
  else:
442
- slice_value = ast.Tuple(elts=arg_nodes, ctx=ast.Load()) # pyright: ignore [reportArgumentType]
449
+ slice_value = ast.Tuple(elts=arg_nodes, ctx=ast.Load())
443
450
 
444
451
  return ast.Subscript(
445
452
  value=ast.Name(id=base_name),
446
- slice=ast.Index(value=slice_value), # pyright: ignore [reportArgumentType]
453
+ slice=slice_value,
447
454
  ctx=ast.Load(),
448
455
  )
449
456
 
@@ -463,16 +470,18 @@ def _get_parent_imports(func: Callable):
463
470
 
464
471
 
465
472
  def _generate_component_create_functiondef(
466
- node: ast.FunctionDef | None,
467
- clz: type[Component] | type[SimpleNamespace],
473
+ clz: type[Component],
468
474
  type_hint_globals: dict[str, Any],
475
+ lineno: int,
476
+ decorator_list: Sequence[ast.expr] = (ast.Name(id="classmethod"),),
469
477
  ) -> ast.FunctionDef:
470
478
  """Generate the create function definition for a Component.
471
479
 
472
480
  Args:
473
- node: The existing create functiondef node from the ast
474
481
  clz: The Component class to generate the create functiondef for.
475
482
  type_hint_globals: The globals to use to resolving a type hint str.
483
+ lineno: The line number to use for the ast nodes.
484
+ decorator_list: The list of decorators to apply to the create functiondef.
476
485
 
477
486
  Returns:
478
487
  The create functiondef node for the ast.
@@ -584,28 +593,26 @@ def _generate_component_create_functiondef(
584
593
  arg=trigger,
585
594
  annotation=ast.Subscript(
586
595
  ast.Name("Optional"),
587
- ast.Index( # pyright: ignore [reportArgumentType]
588
- value=ast.Name(
589
- id=ast.unparse(
590
- figure_out_return_type(
591
- inspect.signature(event_specs).return_annotation
592
- )
593
- if not isinstance(
594
- event_specs := event_triggers[trigger], Sequence
595
- )
596
- else ast.Subscript(
597
- ast.Name("Union"),
598
- ast.Tuple(
599
- [
600
- figure_out_return_type(
601
- inspect.signature(
602
- event_spec
603
- ).return_annotation
604
- )
605
- for event_spec in event_specs
606
- ]
607
- ),
608
- )
596
+ ast.Name(
597
+ id=ast.unparse(
598
+ figure_out_return_type(
599
+ inspect.signature(event_specs).return_annotation
600
+ )
601
+ if not isinstance(
602
+ event_specs := event_triggers[trigger], Sequence
603
+ )
604
+ else ast.Subscript(
605
+ ast.Name("Union"),
606
+ ast.Tuple(
607
+ [
608
+ figure_out_return_type(
609
+ inspect.signature(
610
+ event_spec
611
+ ).return_annotation
612
+ )
613
+ for event_spec in event_specs
614
+ ]
615
+ ),
609
616
  )
610
617
  )
611
618
  ),
@@ -630,7 +637,7 @@ def _generate_component_create_functiondef(
630
637
  definition = ast.FunctionDef( # pyright: ignore [reportCallIssue]
631
638
  name="create",
632
639
  args=create_args,
633
- body=[ # pyright: ignore [reportArgumentType]
640
+ body=[
634
641
  ast.Expr(
635
642
  value=ast.Constant(
636
643
  value=_generate_docstrings(
@@ -644,25 +651,19 @@ def _generate_component_create_functiondef(
644
651
  ],
645
652
  decorator_list=[
646
653
  ast.Name(id="overload"),
647
- *(
648
- node.decorator_list
649
- if node is not None
650
- else [ast.Name(id="classmethod")]
651
- ),
654
+ *decorator_list,
652
655
  ],
653
- lineno=node.lineno if node is not None else None, # pyright: ignore [reportArgumentType]
656
+ lineno=lineno,
654
657
  returns=ast.Constant(value=clz.__name__),
655
658
  )
656
659
  return definition
657
660
 
658
661
 
659
662
  def _generate_staticmethod_call_functiondef(
660
- node: ast.FunctionDef | None,
663
+ node: ast.ClassDef,
661
664
  clz: type[Component] | type[SimpleNamespace],
662
665
  type_hint_globals: dict[str, Any],
663
666
  ) -> ast.FunctionDef | None:
664
- ...
665
-
666
667
  fullspec = getfullargspec(clz.__call__)
667
668
 
668
669
  call_args = ast.arguments(
@@ -699,7 +700,7 @@ def _generate_staticmethod_call_functiondef(
699
700
  ),
700
701
  ],
701
702
  decorator_list=[ast.Name(id="staticmethod")],
702
- lineno=node.lineno if node is not None else None, # pyright: ignore [reportArgumentType]
703
+ lineno=node.lineno,
703
704
  returns=ast.Constant(
704
705
  value=_get_type_hint(
705
706
  typing.get_type_hints(clz.__call__).get("return", None),
@@ -712,7 +713,7 @@ def _generate_staticmethod_call_functiondef(
712
713
 
713
714
 
714
715
  def _generate_namespace_call_functiondef(
715
- node: ast.ClassDef | None,
716
+ node: ast.ClassDef,
716
717
  clz_name: str,
717
718
  classes: dict[str, type[Component] | type[SimpleNamespace]],
718
719
  type_hint_globals: dict[str, Any],
@@ -736,7 +737,7 @@ def _generate_namespace_call_functiondef(
736
737
  clz = classes[clz_name]
737
738
 
738
739
  if not hasattr(clz.__call__, "__self__"):
739
- return _generate_staticmethod_call_functiondef(node, clz, type_hint_globals) # pyright: ignore [reportArgumentType]
740
+ return _generate_staticmethod_call_functiondef(node, clz, type_hint_globals)
740
741
 
741
742
  # Determine which class is wrapped by the namespace __call__ method
742
743
  component_clz = clz.__call__.__self__
@@ -744,10 +745,14 @@ def _generate_namespace_call_functiondef(
744
745
  if clz.__call__.__func__.__name__ != "create": # pyright: ignore [reportFunctionMemberAccess]
745
746
  return None
746
747
 
748
+ if not issubclass(component_clz, Component):
749
+ return None
750
+
747
751
  definition = _generate_component_create_functiondef(
748
- node=None,
749
- clz=component_clz, # pyright: ignore [reportArgumentType]
752
+ clz=component_clz,
750
753
  type_hint_globals=type_hint_globals,
754
+ lineno=node.lineno,
755
+ decorator_list=[],
751
756
  )
752
757
  definition.name = "__call__"
753
758
 
@@ -804,17 +809,18 @@ class StubGenerator(ast.NodeTransformer):
804
809
  node.body.pop(0)
805
810
  return node
806
811
 
807
- def _current_class_is_component(self) -> bool:
812
+ def _current_class_is_component(self) -> type[Component] | None:
808
813
  """Check if the current class is a Component.
809
814
 
810
815
  Returns:
811
816
  Whether the current class is a Component.
812
817
  """
813
- return (
818
+ if (
814
819
  self.current_class is not None
815
820
  and self.current_class in self.classes
816
- and issubclass(self.classes[self.current_class], Component)
817
- )
821
+ and issubclass((clz := self.classes[self.current_class]), Component)
822
+ ):
823
+ return clz
818
824
 
819
825
  def visit_Module(self, node: ast.Module) -> ast.Module:
820
826
  """Visit a Module node and remove docstring from body.
@@ -916,14 +922,14 @@ class StubGenerator(ast.NodeTransformer):
916
922
  isinstance(child, ast.FunctionDef) and child.name == "create"
917
923
  for child in node.body
918
924
  )
919
- and self._current_class_is_component()
925
+ and (clz := self._current_class_is_component()) is not None
920
926
  ):
921
927
  # Add a new .create FunctionDef since one does not exist.
922
928
  node.body.append(
923
929
  _generate_component_create_functiondef(
924
- node=None,
925
- clz=self.classes[self.current_class],
930
+ clz=clz,
926
931
  type_hint_globals=self.type_hint_globals,
932
+ lineno=node.lineno,
927
933
  )
928
934
  )
929
935
  if call_definition is not None:
@@ -949,9 +955,16 @@ class StubGenerator(ast.NodeTransformer):
949
955
  Returns:
950
956
  The modified FunctionDef node (or None).
951
957
  """
952
- if node.name == "create" and self.current_class in self.classes:
958
+ if (
959
+ node.name == "create"
960
+ and self.current_class in self.classes
961
+ and issubclass((clz := self.classes[self.current_class]), Component)
962
+ ):
953
963
  node = _generate_component_create_functiondef(
954
- node, self.classes[self.current_class], self.type_hint_globals
964
+ clz=clz,
965
+ type_hint_globals=self.type_hint_globals,
966
+ lineno=node.lineno,
967
+ decorator_list=node.decorator_list,
955
968
  )
956
969
  else:
957
970
  if node.name.startswith("_") and node.name != "__call__":
reflex/utils/registry.py CHANGED
@@ -4,6 +4,7 @@ import httpx
4
4
 
5
5
  from reflex.config import environment
6
6
  from reflex.utils import console, net
7
+ from reflex.utils.decorator import once
7
8
 
8
9
 
9
10
  def latency(registry: str) -> int:
@@ -48,15 +49,16 @@ def _get_best_registry() -> str:
48
49
  """
49
50
  console.debug("Getting best registry...")
50
51
  registries = [
51
- "https://registry.npmjs.org",
52
- "https://r.cnpmjs.org",
52
+ ("https://registry.npmjs.org", 1),
53
+ ("https://registry.npmmirror.com", 2),
53
54
  ]
54
55
 
55
- best_registry = min(registries, key=average_latency)
56
+ best_registry = min(registries, key=lambda x: average_latency(x[0]) * x[1])[0]
56
57
  console.debug(f"Best registry: {best_registry}")
57
58
  return best_registry
58
59
 
59
60
 
61
+ @once
60
62
  def get_npm_registry() -> str:
61
63
  """Get npm registry. If environment variable is set, use it first.
62
64
 
@@ -16,7 +16,6 @@ from typing import (
16
16
  Callable,
17
17
  Literal,
18
18
  Sequence,
19
- Set,
20
19
  Type,
21
20
  TypeVar,
22
21
  Union,
@@ -322,7 +321,7 @@ if BaseModelV1 is not BaseModelV2:
322
321
 
323
322
 
324
323
  @serializer
325
- def serialize_set(value: Set) -> list:
324
+ def serialize_set(value: set) -> list:
326
325
  """Serialize a set to a JSON serializable list.
327
326
 
328
327
  Args:
reflex/utils/types.py CHANGED
@@ -140,7 +140,7 @@ def is_generic_alias(cls: GenericType) -> bool:
140
140
  Returns:
141
141
  Whether the class is a generic alias.
142
142
  """
143
- return isinstance(cls, GenericAliasTypes) # pyright: ignore [reportArgumentType]
143
+ return isinstance(cls, GenericAliasTypes)
144
144
 
145
145
 
146
146
  @lru_cache()
@@ -923,7 +923,7 @@ StateBases = get_base_class(StateVar)
923
923
  StateIterBases = get_base_class(StateIterVar)
924
924
 
925
925
 
926
- def safe_issubclass(cls: Type, cls_check: Type | tuple[Type, ...]):
926
+ def safe_issubclass(cls: Any, cls_check: Any | tuple[Any, ...]):
927
927
  """Check if a class is a subclass of another class. Returns False if internal error occurs.
928
928
 
929
929
  Args:
@@ -1047,8 +1047,8 @@ def typehint_issubclass(
1047
1047
 
1048
1048
  # Check if the origin of both types is the same (e.g., list for list[int])
1049
1049
  if not safe_issubclass(
1050
- provided_type_origin or possible_subclass, # pyright: ignore [reportArgumentType]
1051
- accepted_type_origin or possible_superclass, # pyright: ignore [reportArgumentType]
1050
+ provided_type_origin or possible_subclass,
1051
+ accepted_type_origin or possible_superclass,
1052
1052
  ):
1053
1053
  return False
1054
1054
 
reflex/vars/base.py CHANGED
@@ -30,6 +30,7 @@ from typing import (
30
30
  Mapping,
31
31
  NoReturn,
32
32
  ParamSpec,
33
+ Protocol,
33
34
  Sequence,
34
35
  Set,
35
36
  Tuple,
@@ -59,10 +60,11 @@ from reflex.utils.exceptions import (
59
60
  )
60
61
  from reflex.utils.format import format_state_name
61
62
  from reflex.utils.imports import (
63
+ ImmutableImportDict,
62
64
  ImmutableParsedImportDict,
63
65
  ImportDict,
64
66
  ImportVar,
65
- ParsedImportDict,
67
+ ParsedImportTuple,
66
68
  parse_imports,
67
69
  )
68
70
  from reflex.utils.types import (
@@ -122,7 +124,7 @@ class VarData:
122
124
  field_name: str = dataclasses.field(default="")
123
125
 
124
126
  # Imports needed to render this var
125
- imports: ImmutableParsedImportDict = dataclasses.field(default_factory=tuple)
127
+ imports: ParsedImportTuple = dataclasses.field(default_factory=tuple)
126
128
 
127
129
  # Hooks that need to be present in the component to render this var
128
130
  hooks: tuple[str, ...] = dataclasses.field(default_factory=tuple)
@@ -140,7 +142,7 @@ class VarData:
140
142
  self,
141
143
  state: str = "",
142
144
  field_name: str = "",
143
- imports: ImportDict | ParsedImportDict | None = None,
145
+ imports: ImmutableImportDict | ImmutableParsedImportDict | None = None,
144
146
  hooks: Mapping[str, VarData | None] | Sequence[str] | str | None = None,
145
147
  deps: list[Var] | None = None,
146
148
  position: Hooks.HookPosition | None = None,
@@ -161,7 +163,7 @@ class VarData:
161
163
  hooks = [hooks]
162
164
  if not isinstance(hooks, dict):
163
165
  hooks = dict.fromkeys(hooks or [])
164
- immutable_imports: ImmutableParsedImportDict = tuple(
166
+ immutable_imports: ParsedImportTuple = tuple(
165
167
  (k, tuple(v)) for k, v in parse_imports(imports or {}).items()
166
168
  )
167
169
  object.__setattr__(self, "state", state)
@@ -988,7 +990,7 @@ class Var(Generic[VAR_TYPE]):
988
990
  setattr(state, actual_name, value)
989
991
  except ValueError:
990
992
  console.debug(
991
- f"{type(state).__name__}.{self._js_expr}: Failed conversion of {value} to '{self._var_type.__name__}'. Value not set.",
993
+ f"{type(state).__name__}.{self._js_expr}: Failed conversion of {value!s} to '{self._var_type.__name__}'. Value not set.",
992
994
  )
993
995
  else:
994
996
  setattr(state, actual_name, value)
@@ -1058,7 +1060,9 @@ class Var(Generic[VAR_TYPE]):
1058
1060
 
1059
1061
  return boolify(self)
1060
1062
 
1061
- def __and__(self, other: Var | Any) -> Var:
1063
+ def __and__(
1064
+ self, other: Var[OTHER_VAR_TYPE] | Any
1065
+ ) -> Var[VAR_TYPE | OTHER_VAR_TYPE]:
1062
1066
  """Perform a logical AND operation on the current instance and another variable.
1063
1067
 
1064
1068
  Args:
@@ -1069,7 +1073,9 @@ class Var(Generic[VAR_TYPE]):
1069
1073
  """
1070
1074
  return and_operation(self, other)
1071
1075
 
1072
- def __rand__(self, other: Var | Any) -> Var:
1076
+ def __rand__(
1077
+ self, other: Var[OTHER_VAR_TYPE] | Any
1078
+ ) -> Var[VAR_TYPE | OTHER_VAR_TYPE]:
1073
1079
  """Perform a logical AND operation on the current instance and another variable.
1074
1080
 
1075
1081
  Args:
@@ -1080,7 +1086,9 @@ class Var(Generic[VAR_TYPE]):
1080
1086
  """
1081
1087
  return and_operation(other, self)
1082
1088
 
1083
- def __or__(self, other: Var | Any) -> Var:
1089
+ def __or__(
1090
+ self, other: Var[OTHER_VAR_TYPE] | Any
1091
+ ) -> Var[VAR_TYPE | OTHER_VAR_TYPE]:
1084
1092
  """Perform a logical OR operation on the current instance and another variable.
1085
1093
 
1086
1094
  Args:
@@ -1091,7 +1099,9 @@ class Var(Generic[VAR_TYPE]):
1091
1099
  """
1092
1100
  return or_operation(self, other)
1093
1101
 
1094
- def __ror__(self, other: Var | Any) -> Var:
1102
+ def __ror__(
1103
+ self, other: Var[OTHER_VAR_TYPE] | Any
1104
+ ) -> Var[VAR_TYPE | OTHER_VAR_TYPE]:
1095
1105
  """Perform a logical OR operation on the current instance and another variable.
1096
1106
 
1097
1107
  Args:
@@ -1478,7 +1488,7 @@ class LiteralVar(Var):
1478
1488
  _var_literal_subclasses.append((cls, var_subclass))
1479
1489
 
1480
1490
  @classmethod
1481
- def create( # pyright: ignore [reportArgumentType]
1491
+ def _create_literal_var(
1482
1492
  cls,
1483
1493
  value: Any,
1484
1494
  _var_data: VarData | None = None,
@@ -1559,6 +1569,9 @@ class LiteralVar(Var):
1559
1569
  f"Unsupported type {type(value)} for LiteralVar. Tried to create a LiteralVar from {value}."
1560
1570
  )
1561
1571
 
1572
+ if not TYPE_CHECKING:
1573
+ create = _create_literal_var
1574
+
1562
1575
  def __post_init__(self):
1563
1576
  """Post-initialize the var."""
1564
1577
 
@@ -1835,6 +1848,21 @@ class cached_property: # noqa: N801
1835
1848
  cached_property_no_lock = cached_property
1836
1849
 
1837
1850
 
1851
+ class VarProtocol(Protocol):
1852
+ """A protocol for Var."""
1853
+
1854
+ __dataclass_fields__: ClassVar[dict[str, dataclasses.Field[Any]]]
1855
+
1856
+ @property
1857
+ def _js_expr(self) -> str: ...
1858
+
1859
+ @property
1860
+ def _var_type(self) -> types.GenericType: ...
1861
+
1862
+ @property
1863
+ def _var_data(self) -> VarData: ...
1864
+
1865
+
1838
1866
  class CachedVarOperation:
1839
1867
  """Base class for cached var operations to lower boilerplate code."""
1840
1868
 
@@ -1869,7 +1897,7 @@ class CachedVarOperation:
1869
1897
  return self._cached_get_all_var_data
1870
1898
 
1871
1899
  @cached_property_no_lock
1872
- def _cached_get_all_var_data(self) -> VarData | None:
1900
+ def _cached_get_all_var_data(self: VarProtocol) -> VarData | None:
1873
1901
  """Get the cached VarData.
1874
1902
 
1875
1903
  Returns:
@@ -1879,14 +1907,13 @@ class CachedVarOperation:
1879
1907
  *(
1880
1908
  value._get_all_var_data() if isinstance(value, Var) else None
1881
1909
  for value in (
1882
- getattr(self, field.name)
1883
- for field in dataclasses.fields(self) # pyright: ignore [reportArgumentType]
1910
+ getattr(self, field.name) for field in dataclasses.fields(self)
1884
1911
  )
1885
1912
  ),
1886
1913
  self._var_data,
1887
1914
  )
1888
1915
 
1889
- def __hash__(self) -> int:
1916
+ def __hash__(self: DataclassInstance) -> int:
1890
1917
  """Calculate the hash of the object.
1891
1918
 
1892
1919
  Returns:
@@ -1897,14 +1924,16 @@ class CachedVarOperation:
1897
1924
  type(self).__name__,
1898
1925
  *[
1899
1926
  getattr(self, field.name)
1900
- for field in dataclasses.fields(self) # pyright: ignore [reportArgumentType]
1927
+ for field in dataclasses.fields(self)
1901
1928
  if field.name not in ["_js_expr", "_var_data", "_var_type"]
1902
1929
  ],
1903
1930
  )
1904
1931
  )
1905
1932
 
1906
1933
 
1907
- def and_operation(a: Var | Any, b: Var | Any) -> Var:
1934
+ def and_operation(
1935
+ a: Var[VAR_TYPE] | Any, b: Var[OTHER_VAR_TYPE] | Any
1936
+ ) -> Var[VAR_TYPE | OTHER_VAR_TYPE]:
1908
1937
  """Perform a logical AND operation on two variables.
1909
1938
 
1910
1939
  Args:
@@ -1934,7 +1963,9 @@ def _and_operation(a: Var, b: Var):
1934
1963
  )
1935
1964
 
1936
1965
 
1937
- def or_operation(a: Var | Any, b: Var | Any) -> Var:
1966
+ def or_operation(
1967
+ a: Var[VAR_TYPE] | Any, b: Var[OTHER_VAR_TYPE] | Any
1968
+ ) -> Var[VAR_TYPE | OTHER_VAR_TYPE]:
1938
1969
  """Perform a logical OR operation on two variables.
1939
1970
 
1940
1971
  Args:
@@ -2340,7 +2371,7 @@ class ComputedVar(Var[RETURN_TYPE]):
2340
2371
  if not _isinstance(value, self._var_type, nested=1, treat_var_as_type=False):
2341
2372
  console.error(
2342
2373
  f"Computed var '{type(instance).__name__}.{self._js_expr}' must return"
2343
- f" a value of type '{self._var_type}', got '{value}' of type {type(value)}."
2374
+ f" a value of type '{self._var_type}', got '{value!s}' of type {type(value)}."
2344
2375
  )
2345
2376
 
2346
2377
  def _deps(
@@ -2976,7 +3007,7 @@ def get_uuid_string_var() -> Var:
2976
3007
  unique_uuid_var = get_unique_variable_name()
2977
3008
  unique_uuid_var_data = VarData(
2978
3009
  imports={
2979
- f"$/{constants.Dirs.STATE_PATH}": {ImportVar(tag="generateUUID")}, # pyright: ignore [reportArgumentType]
3010
+ f"$/{constants.Dirs.STATE_PATH}": ImportVar(tag="generateUUID"),
2980
3011
  "react": "useMemo",
2981
3012
  },
2982
3013
  hooks={f"const {unique_uuid_var} = useMemo(generateUUID, [])": None},
@@ -3286,10 +3317,15 @@ class Field(Generic[FIELD_TYPE]):
3286
3317
 
3287
3318
  @overload
3288
3319
  def __get__(
3289
- self: Field[int]
3290
- | Field[float]
3320
+ self: Field[int] | Field[int | None],
3321
+ instance: None,
3322
+ owner: Any,
3323
+ ) -> NumberVar[int]: ...
3324
+
3325
+ @overload
3326
+ def __get__(
3327
+ self: Field[float]
3291
3328
  | Field[int | float]
3292
- | Field[int | None]
3293
3329
  | Field[float | None]
3294
3330
  | Field[int | float | None],
3295
3331
  instance: None,
reflex/vars/number.py CHANGED
@@ -16,6 +16,8 @@ from typing import (
16
16
  overload,
17
17
  )
18
18
 
19
+ from typing_extensions import TypeVar as TypeVarExt
20
+
19
21
  from reflex.constants.base import Dirs
20
22
  from reflex.utils.exceptions import (
21
23
  PrimitiveUnserializableToJSONError,
@@ -35,7 +37,9 @@ from .base import (
35
37
  var_operation_return,
36
38
  )
37
39
 
38
- NUMBER_T = TypeVar("NUMBER_T", int, float, bool)
40
+ NUMBER_T = TypeVarExt(
41
+ "NUMBER_T", bound=(int | float), default=(int | float), covariant=True
42
+ )
39
43
 
40
44
  if TYPE_CHECKING:
41
45
  from .sequence import ArrayVar
@@ -313,13 +317,19 @@ class NumberVar(Var[NUMBER_T], python_types=(int, float)):
313
317
  """
314
318
  return self
315
319
 
316
- def __round__(self):
320
+ def __round__(self, ndigits: int | NumberVar = 0) -> NumberVar:
317
321
  """Round the number.
318
322
 
323
+ Args:
324
+ ndigits: The number of digits to round.
325
+
319
326
  Returns:
320
327
  The number round operation.
321
328
  """
322
- return number_round_operation(self)
329
+ if not isinstance(ndigits, NUMBER_TYPES):
330
+ raise_unsupported_operand_types("round", (type(self), type(ndigits)))
331
+
332
+ return number_round_operation(self, +ndigits)
323
333
 
324
334
  def __ceil__(self):
325
335
  """Ceil the number.
@@ -653,16 +663,23 @@ def number_exponent_operation(lhs: NumberVar, rhs: NumberVar):
653
663
 
654
664
 
655
665
  @var_operation
656
- def number_round_operation(value: NumberVar):
666
+ def number_round_operation(value: NumberVar, ndigits: NumberVar | int):
657
667
  """Round the number.
658
668
 
659
669
  Args:
660
670
  value: The number.
671
+ ndigits: The number of digits.
661
672
 
662
673
  Returns:
663
674
  The number round operation.
664
675
  """
665
- return var_operation_return(js_expression=f"Math.round({value})", var_type=int)
676
+ if (isinstance(ndigits, LiteralNumberVar) and ndigits._var_value == 0) or (
677
+ isinstance(ndigits, int) and ndigits == 0
678
+ ):
679
+ return var_operation_return(js_expression=f"Math.round({value})", var_type=int)
680
+ return var_operation_return(
681
+ js_expression=f"(+{value}.toFixed({ndigits}))", var_type=float
682
+ )
666
683
 
667
684
 
668
685
  @var_operation
@@ -1064,7 +1081,7 @@ U = TypeVar("U")
1064
1081
 
1065
1082
  @var_operation
1066
1083
  def ternary_operation(
1067
- condition: BooleanVar, if_true: Var[T], if_false: Var[U]
1084
+ condition: Var[bool], if_true: Var[T], if_false: Var[U]
1068
1085
  ) -> CustomVarOperationReturn[T | U]:
1069
1086
  """Create a ternary operation.
1070
1087
 
reflex/vars/sequence.py CHANGED
@@ -1604,6 +1604,8 @@ def _determine_value_of_array_index(
1604
1604
  return args[0] if args else Any
1605
1605
  if origin_var_type is tuple:
1606
1606
  args = get_args(var_type)
1607
+ if len(args) == 2 and args[1] is ...:
1608
+ return args[0]
1607
1609
  return (
1608
1610
  args[int(index) % len(args)]
1609
1611
  if args and index is not None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: reflex
3
- Version: 0.7.4a3
3
+ Version: 0.7.5a1
4
4
  Summary: Web apps in pure Python.
5
5
  Project-URL: homepage, https://reflex.dev
6
6
  Project-URL: repository, https://github.com/reflex-dev/reflex
@@ -23,7 +23,7 @@ Requires-Dist: build<2.0,>=1.0.3
23
23
  Requires-Dist: charset-normalizer<4.0,>=3.3.2
24
24
  Requires-Dist: distro<2.0,>=1.8.0; platform_system == 'Linux'
25
25
  Requires-Dist: fastapi!=0.111.0,!=0.111.1,>=0.96.0
26
- Requires-Dist: granian[reload]>=2.0.0
26
+ Requires-Dist: granian[reload]>=2.2.0
27
27
  Requires-Dist: gunicorn<24.0.0,>=23.0.0
28
28
  Requires-Dist: httpx<1.0,>=0.25.1
29
29
  Requires-Dist: jinja2<4.0,>=3.1.2