reflex 0.6.4a2__py3-none-any.whl → 0.6.5__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 (228) hide show
  1. reflex/.templates/jinja/web/pages/custom_component.js.jinja2 +0 -14
  2. reflex/.templates/jinja/web/pages/utils.js.jinja2 +4 -8
  3. reflex/.templates/web/components/shiki/code.js +16 -11
  4. reflex/.templates/web/utils/state.js +29 -21
  5. reflex/__init__.py +4 -0
  6. reflex/__init__.pyi +4 -0
  7. reflex/app.py +148 -154
  8. reflex/app_mixins/lifespan.py +5 -1
  9. reflex/app_mixins/middleware.py +3 -1
  10. reflex/app_mixins/mixin.py +3 -2
  11. reflex/base.py +2 -4
  12. reflex/compiler/compiler.py +111 -37
  13. reflex/components/base/app_wrap.pyi +17 -17
  14. reflex/components/base/bare.py +72 -3
  15. reflex/components/base/body.pyi +17 -17
  16. reflex/components/base/document.pyi +81 -81
  17. reflex/components/base/error_boundary.pyi +25 -18
  18. reflex/components/base/fragment.pyi +17 -17
  19. reflex/components/base/head.pyi +33 -33
  20. reflex/components/base/link.pyi +33 -33
  21. reflex/components/base/meta.pyi +65 -65
  22. reflex/components/base/script.py +4 -4
  23. reflex/components/base/script.pyi +23 -20
  24. reflex/components/component.py +250 -31
  25. reflex/components/core/banner.py +1 -1
  26. reflex/components/core/banner.pyi +81 -81
  27. reflex/components/core/client_side_routing.pyi +33 -33
  28. reflex/components/core/clipboard.py +2 -2
  29. reflex/components/core/clipboard.pyi +24 -18
  30. reflex/components/core/debounce.py +2 -2
  31. reflex/components/core/debounce.pyi +18 -18
  32. reflex/components/core/html.pyi +17 -17
  33. reflex/components/core/upload.py +90 -28
  34. reflex/components/core/upload.pyi +128 -72
  35. reflex/components/datadisplay/code.py +55 -40
  36. reflex/components/datadisplay/code.pyi +46 -44
  37. reflex/components/datadisplay/dataeditor.py +21 -20
  38. reflex/components/datadisplay/dataeditor.pyi +103 -35
  39. reflex/components/datadisplay/shiki_code_block.py +60 -27
  40. reflex/components/datadisplay/shiki_code_block.pyi +86 -65
  41. reflex/components/dynamic.py +9 -5
  42. reflex/components/el/element.pyi +17 -17
  43. reflex/components/el/elements/base.pyi +17 -17
  44. reflex/components/el/elements/forms.py +12 -3
  45. reflex/components/el/elements/forms.pyi +293 -233
  46. reflex/components/el/elements/inline.pyi +449 -449
  47. reflex/components/el/elements/media.pyi +401 -401
  48. reflex/components/el/elements/metadata.pyi +97 -97
  49. reflex/components/el/elements/other.pyi +113 -113
  50. reflex/components/el/elements/scripts.pyi +49 -49
  51. reflex/components/el/elements/sectioning.pyi +241 -241
  52. reflex/components/el/elements/tables.pyi +161 -161
  53. reflex/components/el/elements/typography.pyi +241 -241
  54. reflex/components/gridjs/datatable.pyi +33 -33
  55. reflex/components/lucide/icon.py +1 -1
  56. reflex/components/lucide/icon.pyi +33 -33
  57. reflex/components/markdown/markdown.py +180 -49
  58. reflex/components/markdown/markdown.pyi +36 -19
  59. reflex/components/moment/moment.py +17 -21
  60. reflex/components/moment/moment.pyi +26 -21
  61. reflex/components/next/base.pyi +17 -17
  62. reflex/components/next/image.py +3 -3
  63. reflex/components/next/image.pyi +21 -19
  64. reflex/components/next/link.pyi +17 -17
  65. reflex/components/next/video.pyi +17 -17
  66. reflex/components/plotly/plotly.py +79 -78
  67. reflex/components/plotly/plotly.pyi +91 -41
  68. reflex/components/props.py +34 -0
  69. reflex/components/radix/primitives/accordion.py +15 -8
  70. reflex/components/radix/primitives/accordion.pyi +121 -118
  71. reflex/components/radix/primitives/base.pyi +33 -33
  72. reflex/components/radix/primitives/drawer.py +41 -20
  73. reflex/components/radix/primitives/drawer.pyi +279 -190
  74. reflex/components/radix/primitives/form.py +2 -2
  75. reflex/components/radix/primitives/form.pyi +200 -167
  76. reflex/components/radix/primitives/progress.pyi +81 -81
  77. reflex/components/radix/primitives/slider.pyi +89 -83
  78. reflex/components/radix/themes/base.py +30 -1
  79. reflex/components/radix/themes/base.pyi +286 -113
  80. reflex/components/radix/themes/color_mode.py +17 -9
  81. reflex/components/radix/themes/color_mode.pyi +68 -56
  82. reflex/components/radix/themes/components/alert_dialog.py +8 -5
  83. reflex/components/radix/themes/components/alert_dialog.pyi +125 -117
  84. reflex/components/radix/themes/components/aspect_ratio.pyi +17 -17
  85. reflex/components/radix/themes/components/avatar.py +1 -5
  86. reflex/components/radix/themes/components/avatar.pyi +17 -17
  87. reflex/components/radix/themes/components/badge.py +1 -5
  88. reflex/components/radix/themes/components/badge.pyi +17 -17
  89. reflex/components/radix/themes/components/button.pyi +18 -21
  90. reflex/components/radix/themes/components/callout.py +1 -4
  91. reflex/components/radix/themes/components/callout.pyi +81 -81
  92. reflex/components/radix/themes/components/card.py +1 -3
  93. reflex/components/radix/themes/components/card.pyi +17 -17
  94. reflex/components/radix/themes/components/checkbox.py +4 -8
  95. reflex/components/radix/themes/components/checkbox.pyi +61 -52
  96. reflex/components/radix/themes/components/checkbox_cards.pyi +33 -33
  97. reflex/components/radix/themes/components/checkbox_group.pyi +33 -33
  98. reflex/components/radix/themes/components/context_menu.py +121 -28
  99. reflex/components/radix/themes/components/context_menu.pyi +250 -147
  100. reflex/components/radix/themes/components/data_list.pyi +65 -65
  101. reflex/components/radix/themes/components/dialog.py +11 -11
  102. reflex/components/radix/themes/components/dialog.pyi +135 -120
  103. reflex/components/radix/themes/components/dropdown_menu.py +14 -25
  104. reflex/components/radix/themes/components/dropdown_menu.pyi +157 -145
  105. reflex/components/radix/themes/components/hover_card.py +19 -7
  106. reflex/components/radix/themes/components/hover_card.pyi +102 -67
  107. reflex/components/radix/themes/components/icon_button.pyi +18 -21
  108. reflex/components/radix/themes/components/inset.py +1 -3
  109. reflex/components/radix/themes/components/inset.pyi +17 -17
  110. reflex/components/radix/themes/components/popover.py +22 -13
  111. reflex/components/radix/themes/components/popover.pyi +98 -72
  112. reflex/components/radix/themes/components/progress.pyi +17 -17
  113. reflex/components/radix/themes/components/radio.pyi +17 -17
  114. reflex/components/radix/themes/components/radio_cards.py +2 -2
  115. reflex/components/radix/themes/components/radio_cards.pyi +37 -34
  116. reflex/components/radix/themes/components/radio_group.py +3 -7
  117. reflex/components/radix/themes/components/radio_group.pyi +69 -66
  118. reflex/components/radix/themes/components/scroll_area.py +1 -3
  119. reflex/components/radix/themes/components/scroll_area.pyi +17 -17
  120. reflex/components/radix/themes/components/segmented_control.pyi +37 -34
  121. reflex/components/radix/themes/components/select.py +7 -11
  122. reflex/components/radix/themes/components/select.pyi +175 -154
  123. reflex/components/radix/themes/components/separator.py +1 -4
  124. reflex/components/radix/themes/components/separator.pyi +17 -17
  125. reflex/components/radix/themes/components/skeleton.pyi +17 -17
  126. reflex/components/radix/themes/components/slider.py +12 -21
  127. reflex/components/radix/themes/components/slider.pyi +47 -25
  128. reflex/components/radix/themes/components/spinner.py +1 -4
  129. reflex/components/radix/themes/components/spinner.pyi +17 -17
  130. reflex/components/radix/themes/components/switch.py +3 -6
  131. reflex/components/radix/themes/components/switch.pyi +21 -18
  132. reflex/components/radix/themes/components/table.py +21 -5
  133. reflex/components/radix/themes/components/table.pyi +392 -116
  134. reflex/components/radix/themes/components/tabs.py +3 -6
  135. reflex/components/radix/themes/components/tabs.pyi +89 -83
  136. reflex/components/radix/themes/components/text_area.py +1 -5
  137. reflex/components/radix/themes/components/text_area.pyi +43 -20
  138. reflex/components/radix/themes/components/text_field.py +1 -5
  139. reflex/components/radix/themes/components/text_field.pyi +101 -55
  140. reflex/components/radix/themes/components/tooltip.py +5 -7
  141. reflex/components/radix/themes/components/tooltip.pyi +25 -22
  142. reflex/components/radix/themes/layout/base.py +2 -27
  143. reflex/components/radix/themes/layout/base.pyi +82 -82
  144. reflex/components/radix/themes/layout/box.pyi +17 -17
  145. reflex/components/radix/themes/layout/center.pyi +17 -17
  146. reflex/components/radix/themes/layout/container.pyi +17 -17
  147. reflex/components/radix/themes/layout/flex.py +1 -6
  148. reflex/components/radix/themes/layout/flex.pyi +17 -17
  149. reflex/components/radix/themes/layout/grid.py +1 -6
  150. reflex/components/radix/themes/layout/grid.pyi +17 -17
  151. reflex/components/radix/themes/layout/list.py +20 -15
  152. reflex/components/radix/themes/layout/list.pyi +175 -92
  153. reflex/components/radix/themes/layout/section.pyi +17 -17
  154. reflex/components/radix/themes/layout/spacer.pyi +17 -17
  155. reflex/components/radix/themes/layout/stack.py +6 -6
  156. reflex/components/radix/themes/layout/stack.pyi +91 -62
  157. reflex/components/radix/themes/typography/blockquote.py +2 -8
  158. reflex/components/radix/themes/typography/blockquote.pyi +17 -17
  159. reflex/components/radix/themes/typography/code.py +4 -10
  160. reflex/components/radix/themes/typography/code.pyi +19 -18
  161. reflex/components/radix/themes/typography/heading.py +4 -11
  162. reflex/components/radix/themes/typography/heading.pyi +19 -18
  163. reflex/components/radix/themes/typography/link.py +4 -10
  164. reflex/components/radix/themes/typography/link.pyi +19 -18
  165. reflex/components/radix/themes/typography/text.py +4 -11
  166. reflex/components/radix/themes/typography/text.pyi +115 -114
  167. reflex/components/react_player/audio.pyi +58 -33
  168. reflex/components/react_player/react_player.py +17 -17
  169. reflex/components/react_player/react_player.pyi +55 -33
  170. reflex/components/react_player/video.pyi +58 -33
  171. reflex/components/recharts/cartesian.py +45 -45
  172. reflex/components/recharts/cartesian.pyi +389 -304
  173. reflex/components/recharts/charts.py +22 -22
  174. reflex/components/recharts/charts.pyi +226 -179
  175. reflex/components/recharts/general.py +26 -27
  176. reflex/components/recharts/general.pyi +106 -99
  177. reflex/components/recharts/polar.py +33 -33
  178. reflex/components/recharts/polar.pyi +70 -64
  179. reflex/components/recharts/recharts.pyi +33 -33
  180. reflex/components/sonner/toast.py +9 -36
  181. reflex/components/sonner/toast.pyi +20 -24
  182. reflex/components/suneditor/editor.py +8 -8
  183. reflex/components/suneditor/editor.pyi +50 -25
  184. reflex/components/tags/iter_tag.py +1 -10
  185. reflex/components/tags/tag.py +1 -4
  186. reflex/config.py +252 -41
  187. reflex/constants/__init__.py +4 -16
  188. reflex/constants/base.py +7 -14
  189. reflex/constants/colors.py +0 -1
  190. reflex/constants/installer.py +12 -7
  191. reflex/constants/state.py +4 -0
  192. reflex/custom_components/custom_components.py +6 -6
  193. reflex/event.py +486 -241
  194. reflex/experimental/client_state.py +9 -9
  195. reflex/experimental/layout.py +2 -2
  196. reflex/experimental/layout.pyi +95 -87
  197. reflex/experimental/misc.py +1 -1
  198. reflex/istate/__init__.py +1 -0
  199. reflex/istate/proxy.py +33 -0
  200. reflex/istate/wrappers.py +27 -0
  201. reflex/model.py +7 -7
  202. reflex/page.py +2 -1
  203. reflex/reflex.py +142 -8
  204. reflex/state.py +133 -46
  205. reflex/testing.py +9 -7
  206. reflex/utils/console.py +0 -1
  207. reflex/utils/exceptions.py +31 -3
  208. reflex/utils/exec.py +33 -14
  209. reflex/utils/format.py +15 -12
  210. reflex/utils/net.py +1 -1
  211. reflex/utils/path_ops.py +2 -2
  212. reflex/utils/prerequisites.py +82 -46
  213. reflex/utils/pyi_generator.py +63 -20
  214. reflex/utils/registry.py +1 -1
  215. reflex/utils/serializers.py +75 -36
  216. reflex/utils/telemetry.py +3 -2
  217. reflex/utils/types.py +125 -10
  218. reflex/vars/base.py +131 -119
  219. reflex/vars/function.py +59 -12
  220. reflex/vars/number.py +3 -1
  221. reflex/vars/object.py +30 -24
  222. reflex/vars/sequence.py +7 -7
  223. {reflex-0.6.4a2.dist-info → reflex-0.6.5.dist-info}/METADATA +3 -3
  224. reflex-0.6.5.dist-info/RECORD +394 -0
  225. reflex-0.6.4a2.dist-info/RECORD +0 -391
  226. {reflex-0.6.4a2.dist-info → reflex-0.6.5.dist-info}/LICENSE +0 -0
  227. {reflex-0.6.4a2.dist-info → reflex-0.6.5.dist-info}/WHEEL +0 -0
  228. {reflex-0.6.4a2.dist-info → reflex-0.6.5.dist-info}/entry_points.txt +0 -0
reflex/vars/base.py CHANGED
@@ -38,13 +38,7 @@ from typing import (
38
38
  overload,
39
39
  )
40
40
 
41
- from typing_extensions import (
42
- ParamSpec,
43
- TypeGuard,
44
- deprecated,
45
- get_type_hints,
46
- override,
47
- )
41
+ from typing_extensions import ParamSpec, TypeGuard, deprecated, get_type_hints, override
48
42
 
49
43
  from reflex import constants
50
44
  from reflex.base import Base
@@ -63,16 +57,19 @@ from reflex.utils.imports import (
63
57
  ParsedImportDict,
64
58
  parse_imports,
65
59
  )
66
- from reflex.utils.types import GenericType, Self, get_origin, has_args, unionize
60
+ from reflex.utils.types import (
61
+ GenericType,
62
+ Self,
63
+ _isinstance,
64
+ get_origin,
65
+ has_args,
66
+ unionize,
67
+ )
67
68
 
68
69
  if TYPE_CHECKING:
69
70
  from reflex.state import BaseState
70
71
 
71
- from .function import FunctionVar
72
- from .number import (
73
- BooleanVar,
74
- NumberVar,
75
- )
72
+ from .number import BooleanVar, NumberVar
76
73
  from .object import ObjectVar
77
74
  from .sequence import ArrayVar, StringVar
78
75
 
@@ -151,31 +148,41 @@ class VarData:
151
148
  """
152
149
  return dict((k, list(v)) for k, v in self.imports)
153
150
 
154
- @classmethod
155
- def merge(cls, *others: VarData | None) -> VarData | None:
151
+ def merge(*all: VarData | None) -> VarData | None:
156
152
  """Merge multiple var data objects.
157
153
 
158
154
  Args:
159
- *others: The var data objects to merge.
155
+ *all: The var data objects to merge.
160
156
 
161
157
  Returns:
162
158
  The merged var data object.
159
+
160
+ # noqa: DAR102 *all
163
161
  """
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
- )
162
+ all_var_datas = list(filter(None, all))
163
+
164
+ if not all_var_datas:
165
+ return None
166
+
167
+ if len(all_var_datas) == 1:
168
+ return all_var_datas[0]
169
+
170
+ # Get the first non-empty field name or default to empty string.
171
+ field_name = next(
172
+ (var_data.field_name for var_data in all_var_datas if var_data.field_name),
173
+ "",
174
+ )
175
+
176
+ # Get the first non-empty state or default to empty string.
177
+ state = next(
178
+ (var_data.state for var_data in all_var_datas if var_data.state), ""
179
+ )
180
+
181
+ hooks = {hook: None for var_data in all_var_datas for hook in var_data.hooks}
182
+
183
+ _imports = imports.merge_imports(
184
+ *(var_data.imports for var_data in all_var_datas)
185
+ )
179
186
 
180
187
  if state or _imports or hooks or field_name:
181
188
  return VarData(
@@ -184,6 +191,7 @@ class VarData:
184
191
  imports=_imports,
185
192
  hooks=hooks,
186
193
  )
194
+
187
195
  return None
188
196
 
189
197
  def __bool__(self) -> bool:
@@ -261,6 +269,24 @@ def _decode_var_immutable(value: str) -> tuple[VarData | None, str]:
261
269
  return VarData.merge(*var_datas) if var_datas else None, value
262
270
 
263
271
 
272
+ def can_use_in_object_var(cls: GenericType) -> bool:
273
+ """Check if the class can be used in an ObjectVar.
274
+
275
+ Args:
276
+ cls: The class to check.
277
+
278
+ Returns:
279
+ Whether the class can be used in an ObjectVar.
280
+ """
281
+ if types.is_union(cls):
282
+ return all(can_use_in_object_var(t) for t in types.get_args(cls))
283
+ return (
284
+ inspect.isclass(cls)
285
+ and not issubclass(cls, Var)
286
+ and serializers.can_serialize(cls, dict)
287
+ )
288
+
289
+
264
290
  @dataclasses.dataclass(
265
291
  eq=False,
266
292
  frozen=True,
@@ -547,36 +573,33 @@ class Var(Generic[VAR_TYPE]):
547
573
  # Encode the _var_data into the formatted output for tracking purposes.
548
574
  return f"{constants.REFLEX_VAR_OPENING_TAG}{hashed_var}{constants.REFLEX_VAR_CLOSING_TAG}{self._js_expr}"
549
575
 
550
- @overload
551
- def to(self, output: Type[StringVar]) -> StringVar: ...
552
-
553
576
  @overload
554
577
  def to(self, output: Type[str]) -> StringVar: ...
555
578
 
556
579
  @overload
557
- def to(self, output: Type[BooleanVar]) -> BooleanVar: ...
580
+ def to(self, output: Type[bool]) -> BooleanVar: ...
558
581
 
559
582
  @overload
560
- def to(
561
- self, output: Type[NumberVar], var_type: type[int] | type[float] = float
562
- ) -> NumberVar: ...
583
+ def to(self, output: type[int] | type[float]) -> NumberVar: ...
563
584
 
564
585
  @overload
565
586
  def to(
566
587
  self,
567
- output: Type[ArrayVar],
568
- var_type: type[list] | type[tuple] | type[set] = list,
588
+ output: type[list] | type[tuple] | type[set],
569
589
  ) -> ArrayVar: ...
570
590
 
571
591
  @overload
572
592
  def to(
573
- self, output: Type[ObjectVar], var_type: types.GenericType = dict
574
- ) -> ObjectVar: ...
593
+ self, output: Type[ObjectVar], var_type: Type[VAR_INSIDE]
594
+ ) -> ObjectVar[VAR_INSIDE]: ...
575
595
 
576
596
  @overload
577
597
  def to(
578
- self, output: Type[FunctionVar], var_type: Type[Callable] = Callable
579
- ) -> FunctionVar: ...
598
+ self, output: Type[ObjectVar], var_type: None = None
599
+ ) -> ObjectVar[VAR_TYPE]: ...
600
+
601
+ @overload
602
+ def to(self, output: VAR_SUBCLASS, var_type: None = None) -> VAR_SUBCLASS: ...
580
603
 
581
604
  @overload
582
605
  def to(
@@ -601,10 +624,6 @@ class Var(Generic[VAR_TYPE]):
601
624
  """
602
625
  from .object import ObjectVar
603
626
 
604
- base_type = var_type
605
- if types.is_optional(base_type):
606
- base_type = types.get_args(base_type)[0]
607
-
608
627
  fixed_output_type = get_origin(output) or output
609
628
 
610
629
  # If the first argument is a python type, we map it to the corresponding Var type.
@@ -616,21 +635,19 @@ class Var(Generic[VAR_TYPE]):
616
635
  return get_to_operation(NoneVar).create(self) # type: ignore
617
636
 
618
637
  # Handle fixed_output_type being Base or a dataclass.
619
- try:
620
- if issubclass(fixed_output_type, Base):
621
- return self.to(ObjectVar, output)
622
- except TypeError:
623
- pass
624
- if dataclasses.is_dataclass(fixed_output_type) and not issubclass(
625
- fixed_output_type, Var
626
- ):
638
+ if can_use_in_object_var(fixed_output_type):
627
639
  return self.to(ObjectVar, output)
628
640
 
629
641
  if inspect.isclass(output):
630
642
  for var_subclass in _var_subclasses[::-1]:
631
643
  if issubclass(output, var_subclass.var_subclass):
644
+ current_var_type = self._var_type
645
+ if current_var_type is Any:
646
+ new_var_type = var_type
647
+ else:
648
+ new_var_type = var_type or current_var_type
632
649
  to_operation_return = var_subclass.to_var_subclass.create(
633
- value=self, _var_type=var_type
650
+ value=self, _var_type=new_var_type
634
651
  )
635
652
  return to_operation_return # type: ignore
636
653
 
@@ -693,11 +710,7 @@ class Var(Generic[VAR_TYPE]):
693
710
  ):
694
711
  return self.to(NumberVar, self._var_type)
695
712
 
696
- if all(
697
- inspect.isclass(t)
698
- and (issubclass(t, Base) or dataclasses.is_dataclass(t))
699
- for t in inner_types
700
- ):
713
+ if can_use_in_object_var(var_type):
701
714
  return self.to(ObjectVar, self._var_type)
702
715
 
703
716
  return self
@@ -716,16 +729,12 @@ class Var(Generic[VAR_TYPE]):
716
729
  if issubclass(fixed_type, var_subclass.python_types):
717
730
  return self.to(var_subclass.var_subclass, self._var_type)
718
731
 
719
- try:
720
- if issubclass(fixed_type, Base):
721
- return self.to(ObjectVar, self._var_type)
722
- except TypeError:
723
- pass
724
- if dataclasses.is_dataclass(fixed_type):
732
+ if can_use_in_object_var(fixed_type):
725
733
  return self.to(ObjectVar, self._var_type)
734
+
726
735
  return self
727
736
 
728
- def get_default_value(self) -> Any:
737
+ def _get_default_value(self) -> Any:
729
738
  """Get the default value of the var.
730
739
 
731
740
  Returns:
@@ -768,7 +777,7 @@ class Var(Generic[VAR_TYPE]):
768
777
  ) from e
769
778
  return set() if issubclass(type_, set) else None
770
779
 
771
- def get_setter_name(self, include_state: bool = True) -> str:
780
+ def _get_setter_name(self, include_state: bool = True) -> str:
772
781
  """Get the name of the var's generated setter function.
773
782
 
774
783
  Args:
@@ -785,7 +794,7 @@ class Var(Generic[VAR_TYPE]):
785
794
  return setter
786
795
  return ".".join((var_data.state, setter))
787
796
 
788
- def get_setter(self) -> Callable[[BaseState, Any], None]:
797
+ def _get_setter(self) -> Callable[[BaseState, Any], None]:
789
798
  """Get the var's setter function.
790
799
 
791
800
  Returns:
@@ -811,7 +820,7 @@ class Var(Generic[VAR_TYPE]):
811
820
  else:
812
821
  setattr(state, actual_name, value)
813
822
 
814
- setter.__qualname__ = self.get_setter_name()
823
+ setter.__qualname__ = self._get_setter_name()
815
824
 
816
825
  return setter
817
826
 
@@ -944,7 +953,7 @@ class Var(Generic[VAR_TYPE]):
944
953
  else PROTOTYPE_TO_STRING.call(self).to(StringVar)
945
954
  )
946
955
 
947
- def as_ref(self) -> Var:
956
+ def _as_ref(self) -> Var:
948
957
  """Get a reference to the var.
949
958
 
950
959
  Returns:
@@ -989,7 +998,7 @@ class Var(Generic[VAR_TYPE]):
989
998
  type_of = FunctionStringVar("typeof")
990
999
  return type_of.call(self).to(StringVar)
991
1000
 
992
- def without_data(self):
1001
+ def _without_data(self):
993
1002
  """Create a copy of the var without the data.
994
1003
 
995
1004
  Returns:
@@ -997,20 +1006,6 @@ class Var(Generic[VAR_TYPE]):
997
1006
  """
998
1007
  return dataclasses.replace(self, _var_data=None)
999
1008
 
1000
- def contains(self, value: Any = None, field: Any = None):
1001
- """Get an attribute of the var.
1002
-
1003
- Args:
1004
- value: The value to check for.
1005
- field: The field to check for.
1006
-
1007
- Raises:
1008
- TypeError: If the var does not support contains check.
1009
- """
1010
- raise TypeError(
1011
- f"Var of type {self._var_type} does not support contains check."
1012
- )
1013
-
1014
1009
  def __get__(self, instance: Any, owner: Any):
1015
1010
  """Get the var.
1016
1011
 
@@ -1023,14 +1018,6 @@ class Var(Generic[VAR_TYPE]):
1023
1018
  """
1024
1019
  return self
1025
1020
 
1026
- def reverse(self):
1027
- """Reverse the var.
1028
-
1029
- Raises:
1030
- TypeError: If the var does not support reverse.
1031
- """
1032
- raise TypeError("Cannot reverse non-list var.")
1033
-
1034
1021
  def __getattr__(self, name: str):
1035
1022
  """Get an attribute of the var.
1036
1023
 
@@ -1047,6 +1034,13 @@ class Var(Generic[VAR_TYPE]):
1047
1034
  if name.startswith("_"):
1048
1035
  return self.__getattribute__(name)
1049
1036
 
1037
+ if name == "contains":
1038
+ raise TypeError(
1039
+ f"Var of type {self._var_type} does not support contains check."
1040
+ )
1041
+ if name == "reverse":
1042
+ raise TypeError("Cannot reverse non-list var.")
1043
+
1050
1044
  if self._var_type is Any:
1051
1045
  raise TypeError(
1052
1046
  f"You must provide an annotation for the state var `{str(self)}`. Annotation cannot be `{self._var_type}`."
@@ -1075,10 +1069,7 @@ class Var(Generic[VAR_TYPE]):
1075
1069
  try:
1076
1070
  return json.loads(str(self))
1077
1071
  except ValueError:
1078
- try:
1079
- return json.loads(self.json())
1080
- except (ValueError, NotImplementedError):
1081
- return str(self)
1072
+ return str(self)
1082
1073
 
1083
1074
  @property
1084
1075
  def _var_state(self) -> str:
@@ -1156,17 +1147,12 @@ class Var(Generic[VAR_TYPE]):
1156
1147
  "'in' operator not supported for Var types, use Var.contains() instead."
1157
1148
  )
1158
1149
 
1159
- def json(self) -> str:
1160
- """Serialize the var to a JSON string.
1161
-
1162
- Raises:
1163
- NotImplementedError: If the method is not implemented.
1164
- """
1165
- raise NotImplementedError("Var subclasses must implement the json method.")
1166
-
1167
1150
 
1168
1151
  OUTPUT = TypeVar("OUTPUT", bound=Var)
1169
1152
 
1153
+ VAR_SUBCLASS = TypeVar("VAR_SUBCLASS", bound=Var)
1154
+ VAR_INSIDE = TypeVar("VAR_INSIDE")
1155
+
1170
1156
 
1171
1157
  class ToOperation:
1172
1158
  """A var operation that converts a var to another type."""
@@ -1822,6 +1808,14 @@ class ComputedVar(Var[RETURN_TYPE]):
1822
1808
  "return", Any
1823
1809
  )
1824
1810
 
1811
+ if hint is Any:
1812
+ console.deprecate(
1813
+ "untyped-computed-var",
1814
+ "ComputedVar should have a return type annotation.",
1815
+ "0.6.5",
1816
+ "0.7.0",
1817
+ )
1818
+
1825
1819
  kwargs.setdefault("_js_expr", fget.__name__)
1826
1820
  kwargs.setdefault("_var_type", hint)
1827
1821
 
@@ -2015,17 +2009,28 @@ class ComputedVar(Var[RETURN_TYPE]):
2015
2009
  )
2016
2010
 
2017
2011
  if not self._cache:
2018
- return self.fget(instance)
2019
-
2020
- # handle caching
2021
- if not hasattr(instance, self._cache_attr) or self.needs_update(instance):
2022
- # Set cache attr on state instance.
2023
- setattr(instance, self._cache_attr, self.fget(instance))
2024
- # Ensure the computed var gets serialized to redis.
2025
- instance._was_touched = True
2026
- # Set the last updated timestamp on the state instance.
2027
- setattr(instance, self._last_updated_attr, datetime.datetime.now())
2028
- return getattr(instance, self._cache_attr)
2012
+ value = self.fget(instance)
2013
+ else:
2014
+ # handle caching
2015
+ if not hasattr(instance, self._cache_attr) or self.needs_update(instance):
2016
+ # Set cache attr on state instance.
2017
+ setattr(instance, self._cache_attr, self.fget(instance))
2018
+ # Ensure the computed var gets serialized to redis.
2019
+ instance._was_touched = True
2020
+ # Set the last updated timestamp on the state instance.
2021
+ setattr(instance, self._last_updated_attr, datetime.datetime.now())
2022
+ value = getattr(instance, self._cache_attr)
2023
+
2024
+ if not _isinstance(value, self._var_type):
2025
+ console.deprecate(
2026
+ "mismatched-computed-var-return",
2027
+ f"Computed var {type(instance).__name__}.{self._js_expr} returned value of type {type(value)}, "
2028
+ f"expected {self._var_type}. This might cause unexpected behavior.",
2029
+ "0.6.5",
2030
+ "0.7.0",
2031
+ )
2032
+
2033
+ return value
2029
2034
 
2030
2035
  def _deps(
2031
2036
  self,
@@ -2855,6 +2860,8 @@ def dispatch(
2855
2860
 
2856
2861
  V = TypeVar("V")
2857
2862
 
2863
+ BASE_TYPE = TypeVar("BASE_TYPE", bound=Base)
2864
+
2858
2865
 
2859
2866
  class Field(Generic[T]):
2860
2867
  """Shadow class for Var to allow for type hinting in the IDE."""
@@ -2891,6 +2898,11 @@ class Field(Generic[T]):
2891
2898
  self: Field[Dict[str, V]], instance: None, owner
2892
2899
  ) -> ObjectVar[Dict[str, V]]: ...
2893
2900
 
2901
+ @overload
2902
+ def __get__(
2903
+ self: Field[BASE_TYPE], instance: None, owner
2904
+ ) -> ObjectVar[BASE_TYPE]: ...
2905
+
2894
2906
  @overload
2895
2907
  def __get__(self, instance: None, owner) -> Var[T]: ...
2896
2908
 
reflex/vars/function.py CHANGED
@@ -4,17 +4,12 @@ from __future__ import annotations
4
4
 
5
5
  import dataclasses
6
6
  import sys
7
- from typing import Any, Callable, Optional, Tuple, Type, Union
7
+ from typing import Any, Callable, Optional, Sequence, Tuple, Type, Union
8
8
 
9
+ from reflex.utils import format
9
10
  from reflex.utils.types import GenericType
10
11
 
11
- from .base import (
12
- CachedVarOperation,
13
- LiteralVar,
14
- Var,
15
- VarData,
16
- cached_property_no_lock,
17
- )
12
+ from .base import CachedVarOperation, LiteralVar, Var, VarData, cached_property_no_lock
18
13
 
19
14
 
20
15
  class FunctionVar(Var[Callable], python_types=Callable):
@@ -132,6 +127,36 @@ class VarOperationCall(CachedVarOperation, Var):
132
127
  )
133
128
 
134
129
 
130
+ @dataclasses.dataclass(frozen=True)
131
+ class DestructuredArg:
132
+ """Class for destructured arguments."""
133
+
134
+ fields: Tuple[str, ...] = tuple()
135
+ rest: Optional[str] = None
136
+
137
+ def to_javascript(self) -> str:
138
+ """Convert the destructured argument to JavaScript.
139
+
140
+ Returns:
141
+ The destructured argument in JavaScript.
142
+ """
143
+ return format.wrap(
144
+ ", ".join(self.fields) + (f", ...{self.rest}" if self.rest else ""),
145
+ "{",
146
+ "}",
147
+ )
148
+
149
+
150
+ @dataclasses.dataclass(
151
+ frozen=True,
152
+ )
153
+ class FunctionArgs:
154
+ """Class for function arguments."""
155
+
156
+ args: Tuple[Union[str, DestructuredArg], ...] = tuple()
157
+ rest: Optional[str] = None
158
+
159
+
135
160
  @dataclasses.dataclass(
136
161
  eq=False,
137
162
  frozen=True,
@@ -140,8 +165,9 @@ class VarOperationCall(CachedVarOperation, Var):
140
165
  class ArgsFunctionOperation(CachedVarOperation, FunctionVar):
141
166
  """Base class for immutable function defined via arguments and return expression."""
142
167
 
143
- _args_names: Tuple[str, ...] = dataclasses.field(default_factory=tuple)
168
+ _args: FunctionArgs = dataclasses.field(default_factory=FunctionArgs)
144
169
  _return_expr: Union[Var, Any] = dataclasses.field(default=None)
170
+ _explicit_return: bool = dataclasses.field(default=False)
145
171
 
146
172
  @cached_property_no_lock
147
173
  def _cached_var_name(self) -> str:
@@ -150,13 +176,31 @@ class ArgsFunctionOperation(CachedVarOperation, FunctionVar):
150
176
  Returns:
151
177
  The name of the var.
152
178
  """
153
- return f"(({', '.join(self._args_names)}) => ({str(LiteralVar.create(self._return_expr))}))"
179
+ arg_names_str = ", ".join(
180
+ [
181
+ arg if isinstance(arg, str) else arg.to_javascript()
182
+ for arg in self._args.args
183
+ ]
184
+ ) + (f", ...{self._args.rest}" if self._args.rest else "")
185
+
186
+ return_expr_str = str(LiteralVar.create(self._return_expr))
187
+
188
+ # Wrap return expression in curly braces if explicit return syntax is used.
189
+ return_expr_str_wrapped = (
190
+ format.wrap(return_expr_str, "{", "}")
191
+ if self._explicit_return
192
+ else return_expr_str
193
+ )
194
+
195
+ return f"(({arg_names_str}) => {return_expr_str_wrapped})"
154
196
 
155
197
  @classmethod
156
198
  def create(
157
199
  cls,
158
- args_names: Tuple[str, ...],
200
+ args_names: Sequence[Union[str, DestructuredArg]],
159
201
  return_expr: Var | Any,
202
+ rest: str | None = None,
203
+ explicit_return: bool = False,
160
204
  _var_type: GenericType = Callable,
161
205
  _var_data: VarData | None = None,
162
206
  ) -> ArgsFunctionOperation:
@@ -165,6 +209,8 @@ class ArgsFunctionOperation(CachedVarOperation, FunctionVar):
165
209
  Args:
166
210
  args_names: The names of the arguments.
167
211
  return_expr: The return expression of the function.
212
+ rest: The name of the rest argument.
213
+ explicit_return: Whether to use explicit return syntax.
168
214
  _var_data: Additional hooks and imports associated with the Var.
169
215
 
170
216
  Returns:
@@ -174,8 +220,9 @@ class ArgsFunctionOperation(CachedVarOperation, FunctionVar):
174
220
  _js_expr="",
175
221
  _var_type=_var_type,
176
222
  _var_data=_var_data,
177
- _args_names=args_names,
223
+ _args=FunctionArgs(args=tuple(args_names), rest=rest),
178
224
  _return_expr=return_expr,
225
+ _explicit_return=explicit_return,
179
226
  )
180
227
 
181
228
 
reflex/vars/number.py CHANGED
@@ -1116,7 +1116,9 @@ U = TypeVar("U")
1116
1116
 
1117
1117
 
1118
1118
  @var_operation
1119
- def ternary_operation(condition: BooleanVar, if_true: Var[T], if_false: Var[U]):
1119
+ def ternary_operation(
1120
+ condition: BooleanVar, if_true: Var[T], if_false: Var[U]
1121
+ ) -> CustomVarOperationReturn[Union[T, U]]:
1120
1122
  """Create a ternary operation.
1121
1123
 
1122
1124
  Args: