reflex 0.6.0__py3-none-any.whl → 0.6.0a1__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 (253) hide show
  1. reflex/.templates/jinja/custom_components/pyproject.toml.jinja2 +2 -2
  2. reflex/.templates/jinja/web/pages/_app.js.jinja2 +1 -1
  3. reflex/.templates/jinja/web/pages/utils.js.jinja2 +2 -2
  4. reflex/.templates/web/components/reflex/chakra_color_mode_provider.js +36 -0
  5. reflex/.templates/web/utils/state.js +1 -3
  6. reflex/__init__.py +2 -8
  7. reflex/__init__.pyi +1 -2
  8. reflex/app.py +2 -4
  9. reflex/app_module_for_backend.py +1 -1
  10. reflex/base.py +1 -1
  11. reflex/compiler/compiler.py +2 -2
  12. reflex/compiler/utils.py +3 -3
  13. reflex/components/base/app_wrap.py +2 -2
  14. reflex/components/base/app_wrap.pyi +27 -17
  15. reflex/components/base/bare.py +5 -4
  16. reflex/components/base/body.pyi +27 -17
  17. reflex/components/base/document.pyi +131 -81
  18. reflex/components/base/error_boundary.py +7 -6
  19. reflex/components/base/error_boundary.pyi +33 -20
  20. reflex/components/base/fragment.pyi +27 -17
  21. reflex/components/base/head.pyi +53 -33
  22. reflex/components/base/link.py +1 -1
  23. reflex/components/base/link.pyi +54 -33
  24. reflex/components/base/meta.pyi +105 -65
  25. reflex/components/base/script.py +2 -1
  26. reflex/components/base/script.pyi +38 -21
  27. reflex/components/component.py +47 -53
  28. reflex/components/core/banner.py +27 -23
  29. reflex/components/core/banner.pyi +171 -134
  30. reflex/components/core/client_side_routing.py +3 -2
  31. reflex/components/core/client_side_routing.pyi +54 -33
  32. reflex/components/core/clipboard.py +1 -2
  33. reflex/components/core/clipboard.pyi +33 -20
  34. reflex/components/core/cond.py +5 -5
  35. reflex/components/core/debounce.py +5 -5
  36. reflex/components/core/debounce.pyi +33 -20
  37. reflex/components/core/foreach.py +4 -3
  38. reflex/components/core/html.py +1 -1
  39. reflex/components/core/html.pyi +46 -35
  40. reflex/components/core/match.py +17 -17
  41. reflex/components/core/upload.py +23 -17
  42. reflex/components/core/upload.pyi +124 -78
  43. reflex/components/datadisplay/code.py +10 -9
  44. reflex/components/datadisplay/code.pyi +409 -299
  45. reflex/components/datadisplay/dataeditor.py +10 -8
  46. reflex/components/datadisplay/dataeditor.pyi +53 -40
  47. reflex/components/el/element.pyi +27 -17
  48. reflex/components/el/elements/base.py +1 -1
  49. reflex/components/el/elements/base.pyi +45 -34
  50. reflex/components/el/elements/forms.py +16 -16
  51. reflex/components/el/elements/forms.pyi +707 -554
  52. reflex/components/el/elements/inline.py +1 -1
  53. reflex/components/el/elements/inline.pyi +1218 -937
  54. reflex/components/el/elements/media.py +1 -1
  55. reflex/components/el/elements/media.pyi +997 -786
  56. reflex/components/el/elements/metadata.py +6 -3
  57. reflex/components/el/elements/metadata.pyi +242 -181
  58. reflex/components/el/elements/other.py +1 -1
  59. reflex/components/el/elements/other.pyi +306 -235
  60. reflex/components/el/elements/scripts.py +1 -1
  61. reflex/components/el/elements/scripts.pyi +140 -109
  62. reflex/components/el/elements/sectioning.py +2 -0
  63. reflex/components/el/elements/sectioning.pyi +647 -496
  64. reflex/components/el/elements/tables.py +1 -1
  65. reflex/components/el/elements/tables.pyi +452 -351
  66. reflex/components/el/elements/typography.py +1 -1
  67. reflex/components/el/elements/typography.pyi +657 -506
  68. reflex/components/gridjs/datatable.py +9 -6
  69. reflex/components/gridjs/datatable.pyi +56 -35
  70. reflex/components/lucide/icon.py +1 -1
  71. reflex/components/lucide/icon.pyi +54 -33
  72. reflex/components/markdown/markdown.py +31 -26
  73. reflex/components/markdown/markdown.pyi +37 -27
  74. reflex/components/moment/moment.py +12 -13
  75. reflex/components/moment/moment.pyi +35 -23
  76. reflex/components/next/base.pyi +27 -17
  77. reflex/components/next/image.py +1 -1
  78. reflex/components/next/image.pyi +37 -22
  79. reflex/components/next/link.py +1 -1
  80. reflex/components/next/link.pyi +28 -17
  81. reflex/components/next/video.py +1 -1
  82. reflex/components/next/video.pyi +28 -17
  83. reflex/components/plotly/plotly.py +13 -12
  84. reflex/components/plotly/plotly.pyi +54 -39
  85. reflex/components/props.py +1 -1
  86. reflex/components/radix/__init__.pyi +0 -1
  87. reflex/components/radix/primitives/__init__.pyi +0 -1
  88. reflex/components/radix/primitives/accordion.py +4 -4
  89. reflex/components/radix/primitives/accordion.pyi +495 -424
  90. reflex/components/radix/primitives/base.py +1 -1
  91. reflex/components/radix/primitives/base.pyi +54 -33
  92. reflex/components/radix/primitives/drawer.py +1 -1
  93. reflex/components/radix/primitives/drawer.pyi +273 -172
  94. reflex/components/radix/primitives/form.py +1 -1
  95. reflex/components/radix/primitives/form.pyi +364 -257
  96. reflex/components/radix/primitives/progress.py +1 -1
  97. reflex/components/radix/primitives/progress.pyi +282 -231
  98. reflex/components/radix/primitives/slider.py +1 -1
  99. reflex/components/radix/primitives/slider.pyi +138 -87
  100. reflex/components/radix/themes/base.py +24 -3
  101. reflex/components/radix/themes/base.pyi +250 -178
  102. reflex/components/radix/themes/color_mode.py +5 -5
  103. reflex/components/radix/themes/color_mode.pyi +220 -187
  104. reflex/components/radix/themes/components/alert_dialog.py +1 -1
  105. reflex/components/radix/themes/components/alert_dialog.pyi +207 -136
  106. reflex/components/radix/themes/components/aspect_ratio.py +1 -1
  107. reflex/components/radix/themes/components/aspect_ratio.pyi +28 -17
  108. reflex/components/radix/themes/components/avatar.py +1 -1
  109. reflex/components/radix/themes/components/avatar.pyi +81 -70
  110. reflex/components/radix/themes/components/badge.py +1 -1
  111. reflex/components/radix/themes/components/badge.pyi +99 -88
  112. reflex/components/radix/themes/components/button.py +1 -1
  113. reflex/components/radix/themes/components/button.pyi +109 -98
  114. reflex/components/radix/themes/components/callout.py +1 -1
  115. reflex/components/radix/themes/components/callout.pyi +373 -322
  116. reflex/components/radix/themes/components/card.py +1 -1
  117. reflex/components/radix/themes/components/card.pyi +49 -38
  118. reflex/components/radix/themes/components/checkbox.py +2 -1
  119. reflex/components/radix/themes/components/checkbox.pyi +245 -208
  120. reflex/components/radix/themes/components/checkbox_cards.py +1 -1
  121. reflex/components/radix/themes/components/checkbox_cards.pyi +115 -94
  122. reflex/components/radix/themes/components/checkbox_group.py +1 -1
  123. reflex/components/radix/themes/components/checkbox_group.pyi +107 -86
  124. reflex/components/radix/themes/components/context_menu.py +1 -1
  125. reflex/components/radix/themes/components/context_menu.pyi +319 -238
  126. reflex/components/radix/themes/components/data_list.py +1 -1
  127. reflex/components/radix/themes/components/data_list.pyi +171 -130
  128. reflex/components/radix/themes/components/dialog.py +1 -1
  129. reflex/components/radix/themes/components/dialog.pyi +210 -139
  130. reflex/components/radix/themes/components/dropdown_menu.py +1 -1
  131. reflex/components/radix/themes/components/dropdown_menu.pyi +332 -249
  132. reflex/components/radix/themes/components/hover_card.py +1 -1
  133. reflex/components/radix/themes/components/hover_card.pyi +131 -90
  134. reflex/components/radix/themes/components/icon_button.py +3 -2
  135. reflex/components/radix/themes/components/icon_button.pyi +109 -98
  136. reflex/components/radix/themes/components/inset.py +1 -1
  137. reflex/components/radix/themes/components/inset.pyi +58 -47
  138. reflex/components/radix/themes/components/popover.py +1 -1
  139. reflex/components/radix/themes/components/popover.pyi +136 -95
  140. reflex/components/radix/themes/components/progress.py +1 -1
  141. reflex/components/radix/themes/components/progress.pyi +82 -71
  142. reflex/components/radix/themes/components/radio.py +1 -1
  143. reflex/components/radix/themes/components/radio.pyi +80 -69
  144. reflex/components/radix/themes/components/radio_cards.py +1 -1
  145. reflex/components/radix/themes/components/radio_cards.pyi +119 -98
  146. reflex/components/radix/themes/components/radio_group.py +11 -8
  147. reflex/components/radix/themes/components/radio_group.pyi +271 -228
  148. reflex/components/radix/themes/components/scroll_area.py +1 -1
  149. reflex/components/radix/themes/components/scroll_area.pyi +32 -21
  150. reflex/components/radix/themes/components/segmented_control.py +1 -1
  151. reflex/components/radix/themes/components/segmented_control.pyi +113 -90
  152. reflex/components/radix/themes/components/select.py +3 -2
  153. reflex/components/radix/themes/components/select.pyi +471 -374
  154. reflex/components/radix/themes/components/separator.py +2 -1
  155. reflex/components/radix/themes/components/separator.pyi +80 -69
  156. reflex/components/radix/themes/components/skeleton.py +1 -1
  157. reflex/components/radix/themes/components/skeleton.pyi +34 -23
  158. reflex/components/radix/themes/components/slider.py +3 -2
  159. reflex/components/radix/themes/components/slider.pyi +88 -75
  160. reflex/components/radix/themes/components/spinner.py +1 -1
  161. reflex/components/radix/themes/components/spinner.pyi +30 -19
  162. reflex/components/radix/themes/components/switch.py +1 -1
  163. reflex/components/radix/themes/components/switch.pyi +84 -71
  164. reflex/components/radix/themes/components/table.py +1 -1
  165. reflex/components/radix/themes/components/table.pyi +332 -261
  166. reflex/components/radix/themes/components/tabs.py +1 -1
  167. reflex/components/radix/themes/components/tabs.pyi +194 -139
  168. reflex/components/radix/themes/components/text_area.py +1 -1
  169. reflex/components/radix/themes/components/text_area.pyi +111 -96
  170. reflex/components/radix/themes/components/text_field.py +1 -1
  171. reflex/components/radix/themes/components/text_field.pyi +286 -247
  172. reflex/components/radix/themes/components/tooltip.py +1 -1
  173. reflex/components/radix/themes/components/tooltip.pyi +37 -26
  174. reflex/components/radix/themes/layout/__init__.pyi +0 -1
  175. reflex/components/radix/themes/layout/base.py +1 -1
  176. reflex/components/radix/themes/layout/base.pyi +67 -56
  177. reflex/components/radix/themes/layout/box.pyi +45 -34
  178. reflex/components/radix/themes/layout/center.pyi +67 -56
  179. reflex/components/radix/themes/layout/container.py +2 -1
  180. reflex/components/radix/themes/layout/container.pyi +47 -36
  181. reflex/components/radix/themes/layout/flex.py +1 -1
  182. reflex/components/radix/themes/layout/flex.pyi +67 -56
  183. reflex/components/radix/themes/layout/grid.py +1 -1
  184. reflex/components/radix/themes/layout/grid.pyi +75 -64
  185. reflex/components/radix/themes/layout/list.py +6 -5
  186. reflex/components/radix/themes/layout/list.pyi +244 -193
  187. reflex/components/radix/themes/layout/section.py +2 -1
  188. reflex/components/radix/themes/layout/section.pyi +47 -36
  189. reflex/components/radix/themes/layout/spacer.pyi +67 -56
  190. reflex/components/radix/themes/layout/stack.py +1 -1
  191. reflex/components/radix/themes/layout/stack.pyi +159 -128
  192. reflex/components/radix/themes/typography/blockquote.py +1 -1
  193. reflex/components/radix/themes/typography/blockquote.pyi +100 -89
  194. reflex/components/radix/themes/typography/code.py +1 -1
  195. reflex/components/radix/themes/typography/code.pyi +101 -90
  196. reflex/components/radix/themes/typography/heading.py +1 -1
  197. reflex/components/radix/themes/typography/heading.pyi +107 -96
  198. reflex/components/radix/themes/typography/link.py +1 -1
  199. reflex/components/radix/themes/typography/link.pyi +113 -102
  200. reflex/components/radix/themes/typography/text.py +1 -1
  201. reflex/components/radix/themes/typography/text.pyi +572 -501
  202. reflex/components/react_player/audio.pyi +60 -33
  203. reflex/components/react_player/react_player.py +1 -1
  204. reflex/components/react_player/react_player.pyi +60 -33
  205. reflex/components/react_player/video.pyi +60 -33
  206. reflex/components/recharts/cartesian.py +3 -2
  207. reflex/components/recharts/cartesian.pyi +861 -678
  208. reflex/components/recharts/charts.py +5 -4
  209. reflex/components/recharts/charts.pyi +357 -252
  210. reflex/components/recharts/general.py +2 -1
  211. reflex/components/recharts/general.pyi +231 -180
  212. reflex/components/recharts/polar.py +5 -4
  213. reflex/components/recharts/polar.pyi +181 -144
  214. reflex/components/recharts/recharts.pyi +53 -33
  215. reflex/components/sonner/toast.py +17 -16
  216. reflex/components/sonner/toast.pyi +47 -36
  217. reflex/components/suneditor/editor.py +3 -2
  218. reflex/components/suneditor/editor.pyi +78 -55
  219. reflex/components/tags/cond_tag.py +4 -6
  220. reflex/components/tags/iter_tag.py +16 -28
  221. reflex/components/tags/match_tag.py +4 -6
  222. reflex/components/tags/tag.py +23 -40
  223. reflex/custom_components/custom_components.py +1 -3
  224. reflex/event.py +65 -113
  225. reflex/experimental/client_state.py +24 -25
  226. reflex/experimental/hooks.py +16 -16
  227. reflex/experimental/layout.py +5 -5
  228. reflex/experimental/layout.pyi +187 -136
  229. reflex/{vars → ivars}/__init__.py +2 -6
  230. reflex/{vars → ivars}/base.py +216 -599
  231. reflex/{vars → ivars}/function.py +19 -15
  232. reflex/{vars → ivars}/number.py +20 -41
  233. reflex/{vars → ivars}/object.py +30 -28
  234. reflex/{vars → ivars}/sequence.py +50 -53
  235. reflex/middleware/hydrate_middleware.py +0 -2
  236. reflex/middleware/middleware.py +3 -3
  237. reflex/state.py +82 -148
  238. reflex/style.py +22 -21
  239. reflex/utils/exceptions.py +0 -20
  240. reflex/utils/format.py +34 -54
  241. reflex/utils/imports.py +73 -16
  242. reflex/utils/prerequisites.py +15 -35
  243. reflex/utils/pyi_generator.py +8 -13
  244. reflex/utils/serializers.py +22 -12
  245. reflex/utils/telemetry.py +2 -3
  246. reflex/utils/types.py +5 -10
  247. reflex/vars.py +501 -0
  248. {reflex-0.6.0.dist-info → reflex-0.6.0a1.dist-info}/METADATA +5 -4
  249. reflex-0.6.0a1.dist-info/RECORD +384 -0
  250. reflex-0.6.0.dist-info/RECORD +0 -382
  251. {reflex-0.6.0.dist-info → reflex-0.6.0a1.dist-info}/LICENSE +0 -0
  252. {reflex-0.6.0.dist-info → reflex-0.6.0a1.dist-info}/WHEEL +0 -0
  253. {reflex-0.6.0.dist-info → reflex-0.6.0a1.dist-info}/entry_points.txt +0 -0
@@ -9,11 +9,7 @@ import dis
9
9
  import functools
10
10
  import inspect
11
11
  import json
12
- import random
13
- import re
14
- import string
15
12
  import sys
16
- import warnings
17
13
  from types import CodeType, FunctionType
18
14
  from typing import (
19
15
  TYPE_CHECKING,
@@ -21,7 +17,6 @@ from typing import (
21
17
  Callable,
22
18
  Dict,
23
19
  Generic,
24
- Iterable,
25
20
  List,
26
21
  Literal,
27
22
  NoReturn,
@@ -48,14 +43,15 @@ from reflex.utils.exceptions import (
48
43
  VarValueError,
49
44
  )
50
45
  from reflex.utils.format import format_state_name
51
- from reflex.utils.imports import (
52
- ImmutableParsedImportDict,
53
- ImportDict,
54
- ImportVar,
55
- ParsedImportDict,
56
- parse_imports,
57
- )
58
46
  from reflex.utils.types import GenericType, Self, get_origin
47
+ from reflex.vars import (
48
+ REPLACED_NAMES,
49
+ Var,
50
+ VarData,
51
+ _decode_var_immutable,
52
+ _extract_var_data,
53
+ _global_vars,
54
+ )
59
55
 
60
56
  if TYPE_CHECKING:
61
57
  from reflex.state import BaseState
@@ -71,20 +67,19 @@ if TYPE_CHECKING:
71
67
  from .sequence import ArrayVar, StringVar, ToArrayOperation, ToStringOperation
72
68
 
73
69
 
74
- VAR_TYPE = TypeVar("VAR_TYPE", covariant=True)
75
-
76
- warnings.filterwarnings("ignore", message="fields may not start with an underscore")
70
+ VAR_TYPE = TypeVar("VAR_TYPE")
77
71
 
78
72
 
79
73
  @dataclasses.dataclass(
80
74
  eq=False,
81
75
  frozen=True,
76
+ **{"slots": True} if sys.version_info >= (3, 10) else {},
82
77
  )
83
- class Var(Generic[VAR_TYPE]):
78
+ class ImmutableVar(Var, Generic[VAR_TYPE]):
84
79
  """Base class for immutable vars."""
85
80
 
86
81
  # The name of the var.
87
- _js_expr: str = dataclasses.field()
82
+ _var_name: str = dataclasses.field()
88
83
 
89
84
  # The type of the var.
90
85
  _var_type: types.GenericType = dataclasses.field(default=Any)
@@ -98,7 +93,7 @@ class Var(Generic[VAR_TYPE]):
98
93
  Returns:
99
94
  The name of the var.
100
95
  """
101
- return self._js_expr
96
+ return self._var_name
102
97
 
103
98
  @property
104
99
  def _var_is_local(self) -> bool:
@@ -109,26 +104,6 @@ class Var(Generic[VAR_TYPE]):
109
104
  """
110
105
  return False
111
106
 
112
- @property
113
- @deprecated("Use `_js_expr` instead.")
114
- def _var_name(self) -> str:
115
- """The name of the var.
116
-
117
- Returns:
118
- The name of the var.
119
- """
120
- return self._js_expr
121
-
122
- @property
123
- @deprecated("Use `_js_expr` instead.")
124
- def _var_name_unwrapped(self) -> str:
125
- """The name of the var without extra curly braces.
126
-
127
- Returns:
128
- The name of the var.
129
- """
130
- return self._js_expr
131
-
132
107
  @property
133
108
  def _var_is_string(self) -> bool:
134
109
  """Whether the var is a string literal.
@@ -141,11 +116,11 @@ class Var(Generic[VAR_TYPE]):
141
116
  def __post_init__(self):
142
117
  """Post-initialize the var."""
143
118
  # Decode any inline Var markup and apply it to the instance
144
- _var_data, _js_expr = _decode_var_immutable(self._js_expr)
119
+ _var_data, _var_name = _decode_var_immutable(self._var_name)
145
120
 
146
- if _var_data or _js_expr != self._js_expr:
121
+ if _var_data or _var_name != self._var_name:
147
122
  self.__init__(
148
- _js_expr=_js_expr,
123
+ _var_name=_var_name,
149
124
  _var_type=self._var_type,
150
125
  _var_data=VarData.merge(self._var_data, _var_data),
151
126
  )
@@ -156,7 +131,7 @@ class Var(Generic[VAR_TYPE]):
156
131
  Returns:
157
132
  The hash of the var.
158
133
  """
159
- return hash((self._js_expr, self._var_type, self._var_data))
134
+ return hash((self._var_name, self._var_type, self._var_data))
160
135
 
161
136
  def _get_all_var_data(self) -> VarData | None:
162
137
  """Get all VarData associated with the Var.
@@ -166,7 +141,7 @@ class Var(Generic[VAR_TYPE]):
166
141
  """
167
142
  return self._var_data
168
143
 
169
- def equals(self, other: Var) -> bool:
144
+ def equals(self, other: ImmutableVar) -> bool:
170
145
  """Check if two vars are equal.
171
146
 
172
147
  Args:
@@ -176,7 +151,7 @@ class Var(Generic[VAR_TYPE]):
176
151
  Whether the vars are equal.
177
152
  """
178
153
  return (
179
- self._js_expr == other._js_expr
154
+ self._var_name == other._var_name
180
155
  and self._var_type == other._var_type
181
156
  and self._get_all_var_data() == other._get_all_var_data()
182
157
  )
@@ -189,20 +164,24 @@ class Var(Generic[VAR_TYPE]):
189
164
  **kwargs: Var fields to update.
190
165
 
191
166
  Returns:
192
- A new Var with the updated fields overwriting the corresponding fields in this Var.
167
+ A new ImmutableVar with the updated fields overwriting the corresponding fields in this Var.
193
168
 
194
169
  Raises:
195
170
  TypeError: If _var_is_local, _var_is_string, or _var_full_name_needs_state_prefix is not None.
196
171
  """
197
172
  if kwargs.get("_var_is_local", False) is not False:
198
- raise TypeError("The _var_is_local argument is not supported for Var.")
173
+ raise TypeError(
174
+ "The _var_is_local argument is not supported for ImmutableVar."
175
+ )
199
176
 
200
177
  if kwargs.get("_var_is_string", False) is not False:
201
- raise TypeError("The _var_is_string argument is not supported for Var.")
178
+ raise TypeError(
179
+ "The _var_is_string argument is not supported for ImmutableVar."
180
+ )
202
181
 
203
182
  if kwargs.get("_var_full_name_needs_state_prefix", False) is not False:
204
183
  raise TypeError(
205
- "The _var_full_name_needs_state_prefix argument is not supported for Var."
184
+ "The _var_full_name_needs_state_prefix argument is not supported for ImmutableVar."
206
185
  )
207
186
 
208
187
  return dataclasses.replace(
@@ -220,7 +199,7 @@ class Var(Generic[VAR_TYPE]):
220
199
  _var_is_local: bool | None = None,
221
200
  _var_is_string: bool | None = None,
222
201
  _var_data: VarData | None = None,
223
- ) -> Var:
202
+ ) -> ImmutableVar | None:
224
203
  """Create a var from a value.
225
204
 
226
205
  Args:
@@ -231,57 +210,80 @@ class Var(Generic[VAR_TYPE]):
231
210
 
232
211
  Returns:
233
212
  The var.
213
+
214
+ Raises:
215
+ VarTypeError: If the value is JSON-unserializable.
216
+ TypeError: If _var_is_local or _var_is_string is not None.
234
217
  """
235
218
  if _var_is_local is not None:
236
- console.deprecate(
237
- feature_name="_var_is_local",
238
- reason="The _var_is_local argument is not supported for Var."
239
- "If you want to create a Var from a raw Javascript expression, use the constructor directly",
240
- deprecation_version="0.6.0",
241
- removal_version="0.7.0",
219
+ raise TypeError(
220
+ "The _var_is_local argument is not supported for ImmutableVar."
242
221
  )
222
+
243
223
  if _var_is_string is not None:
244
- console.deprecate(
245
- feature_name="_var_is_string",
246
- reason="The _var_is_string argument is not supported for Var."
247
- "If you want to create a Var from a raw Javascript expression, use the constructor directly",
248
- deprecation_version="0.6.0",
249
- removal_version="0.7.0",
224
+ raise TypeError(
225
+ "The _var_is_string argument is not supported for ImmutableVar."
250
226
  )
251
227
 
228
+ from reflex.utils import format
229
+
230
+ # Check for none values.
231
+ if value is None:
232
+ return None
233
+
252
234
  # If the value is already a var, do nothing.
253
- if isinstance(value, Var):
235
+ if isinstance(value, ImmutableVar):
254
236
  return value
255
237
 
256
238
  # Try to pull the imports and hooks from contained values.
257
239
  if not isinstance(value, str):
258
- return LiteralVar.create(value)
240
+ _var_data = VarData.merge(*_extract_var_data(value), _var_data)
259
241
 
260
- if _var_is_string is False or _var_is_local is True:
261
- return cls(
262
- _js_expr=value,
263
- _var_data=_var_data,
242
+ # Try to serialize the value.
243
+ type_ = type(value)
244
+ if type_ in types.JSONType:
245
+ name = value
246
+ else:
247
+ name, _serialized_type = serializers.serialize(value, get_type=True)
248
+ if name is None:
249
+ raise VarTypeError(
250
+ f"No JSON serializer found for var {value} of type {type_}."
264
251
  )
252
+ name = name if isinstance(name, str) else format.json_dumps(name)
265
253
 
266
- return LiteralVar.create(value, _var_data=_var_data)
254
+ return cls(
255
+ _var_name=name,
256
+ _var_type=type_,
257
+ _var_data=_var_data,
258
+ )
267
259
 
268
260
  @classmethod
269
- @deprecated("Use `.create()` instead.")
270
261
  def create_safe(
271
262
  cls,
272
- *args: Any,
273
- **kwargs: Any,
274
- ) -> Var:
275
- """Create a var from a value.
263
+ value: Any,
264
+ _var_is_local: bool | None = None,
265
+ _var_is_string: bool | None = None,
266
+ _var_data: VarData | None = None,
267
+ ) -> ImmutableVar:
268
+ """Create a var from a value, asserting that it is not None.
276
269
 
277
270
  Args:
278
- *args: The arguments to create the var from.
279
- **kwargs: The keyword arguments to create the var from.
271
+ value: The value to create the var from.
272
+ _var_is_local: Whether the var is local. Deprecated.
273
+ _var_is_string: Whether the var is a string literal. Deprecated.
274
+ _var_data: Additional hooks and imports associated with the Var.
280
275
 
281
276
  Returns:
282
277
  The var.
283
278
  """
284
- return cls.create(*args, **kwargs)
279
+ var = cls.create(
280
+ value,
281
+ _var_is_local=_var_is_local,
282
+ _var_is_string=_var_is_string,
283
+ _var_data=_var_data,
284
+ )
285
+ assert var is not None
286
+ return var
285
287
 
286
288
  def __format__(self, format_spec: str) -> str:
287
289
  """Format the var into a Javascript equivalent to an f-string.
@@ -297,7 +299,7 @@ class Var(Generic[VAR_TYPE]):
297
299
  _global_vars[hashed_var] = self
298
300
 
299
301
  # Encode the _var_data into the formatted output for tracking purposes.
300
- return f"{constants.REFLEX_VAR_OPENING_TAG}{hashed_var}{constants.REFLEX_VAR_CLOSING_TAG}{self._js_expr}"
302
+ return f"{constants.REFLEX_VAR_OPENING_TAG}{hashed_var}{constants.REFLEX_VAR_CLOSING_TAG}{self._var_name}"
301
303
 
302
304
  @overload
303
305
  def to(self, output: Type[StringVar]) -> ToStringOperation: ...
@@ -341,7 +343,7 @@ class Var(Generic[VAR_TYPE]):
341
343
  self,
342
344
  output: Type[OUTPUT] | types.GenericType,
343
345
  var_type: types.GenericType | None = None,
344
- ) -> Var:
346
+ ) -> ImmutableVar:
345
347
  """Convert the var to a different type.
346
348
 
347
349
  Args:
@@ -385,12 +387,6 @@ class Var(Generic[VAR_TYPE]):
385
387
  return self.to(BooleanVar, output)
386
388
  if fixed_output_type is None:
387
389
  return ToNoneOperation.create(self)
388
- if issubclass(fixed_output_type, Base):
389
- return self.to(ObjectVar, output)
390
- if dataclasses.is_dataclass(fixed_output_type) and not issubclass(
391
- fixed_output_type, Var
392
- ):
393
- return self.to(ObjectVar, output)
394
390
 
395
391
  if issubclass(output, BooleanVar):
396
392
  return ToBooleanVarOperation.create(self)
@@ -425,9 +421,6 @@ class Var(Generic[VAR_TYPE]):
425
421
  if issubclass(output, (ObjectVar, Base)):
426
422
  return ToObjectOperation.create(self, var_type or dict)
427
423
 
428
- if dataclasses.is_dataclass(output):
429
- return ToObjectOperation.create(self, var_type or dict)
430
-
431
424
  if issubclass(output, FunctionVar):
432
425
  # if fixed_type is not None and not issubclass(fixed_type, Callable):
433
426
  # raise TypeError(
@@ -454,11 +447,11 @@ class Var(Generic[VAR_TYPE]):
454
447
 
455
448
  return self
456
449
 
457
- def guess_type(self) -> Var:
450
+ def guess_type(self) -> ImmutableVar:
458
451
  """Guesses the type of the variable based on its `_var_type` attribute.
459
452
 
460
453
  Returns:
461
- Var: The guessed type of the variable.
454
+ ImmutableVar: The guessed type of the variable.
462
455
 
463
456
  Raises:
464
457
  TypeError: If the type is not supported for guessing.
@@ -486,11 +479,7 @@ class Var(Generic[VAR_TYPE]):
486
479
  ):
487
480
  return self.to(NumberVar, self._var_type)
488
481
 
489
- if all(
490
- inspect.isclass(t)
491
- and (issubclass(t, Base) or dataclasses.is_dataclass(t))
492
- for t in inner_types
493
- ):
482
+ if all(inspect.isclass(t) and issubclass(t, Base) for t in inner_types):
494
483
  return self.to(ObjectVar, self._var_type)
495
484
 
496
485
  return self
@@ -510,8 +499,6 @@ class Var(Generic[VAR_TYPE]):
510
499
  return self.to(StringVar, self._var_type)
511
500
  if issubclass(fixed_type, Base):
512
501
  return self.to(ObjectVar, self._var_type)
513
- if dataclasses.is_dataclass(fixed_type):
514
- return self.to(ObjectVar, self._var_type)
515
502
  return self
516
503
 
517
504
  def get_default_value(self) -> Any:
@@ -566,7 +553,7 @@ class Var(Generic[VAR_TYPE]):
566
553
  Returns:
567
554
  The name of the setter function.
568
555
  """
569
- var_name_parts = self._js_expr.split(".")
556
+ var_name_parts = self._var_name.split(".")
570
557
  setter = constants.SETTER_PREFIX + var_name_parts[-1]
571
558
  var_data = self._get_all_var_data()
572
559
  if var_data is None:
@@ -581,7 +568,7 @@ class Var(Generic[VAR_TYPE]):
581
568
  Returns:
582
569
  A function that that creates a setter for the var.
583
570
  """
584
- actual_name = self._js_expr.split(".")[-1]
571
+ actual_name = self._var_name.split(".")[-1]
585
572
 
586
573
  def setter(state: BaseState, value: Any):
587
574
  """Get the setter for the var.
@@ -596,7 +583,7 @@ class Var(Generic[VAR_TYPE]):
596
583
  setattr(state, actual_name, value)
597
584
  except ValueError:
598
585
  console.debug(
599
- f"{type(state).__name__}.{self._js_expr}: Failed conversion of {value} to '{self._var_type.__name__}'. Value not set.",
586
+ f"{type(state).__name__}.{self._var_name}: Failed conversion of {value} to '{self._var_type.__name__}'. Value not set.",
600
587
  )
601
588
  else:
602
589
  setattr(state, actual_name, value)
@@ -626,11 +613,11 @@ class Var(Generic[VAR_TYPE]):
626
613
  _var_data=VarData.merge(VarData.from_state(state), self._var_data),
627
614
  ).guess_type()
628
615
 
629
- def __eq__(self, other: Var | Any) -> BooleanVar:
616
+ def __eq__(self, other: ImmutableVar | Any) -> BooleanVar:
630
617
  """Check if the current variable is equal to the given variable.
631
618
 
632
619
  Args:
633
- other (Var | Any): The variable to compare with.
620
+ other (ImmutableVar | Any): The variable to compare with.
634
621
 
635
622
  Returns:
636
623
  BooleanVar: A BooleanVar object representing the result of the equality check.
@@ -639,11 +626,11 @@ class Var(Generic[VAR_TYPE]):
639
626
 
640
627
  return equal_operation(self, other)
641
628
 
642
- def __ne__(self, other: Var | Any) -> BooleanVar:
629
+ def __ne__(self, other: ImmutableVar | Any) -> BooleanVar:
643
630
  """Check if the current object is not equal to the given object.
644
631
 
645
632
  Parameters:
646
- other (Var | Any): The object to compare with.
633
+ other (ImmutableVar | Any): The object to compare with.
647
634
 
648
635
  Returns:
649
636
  BooleanVar: A BooleanVar object representing the result of the comparison.
@@ -662,7 +649,7 @@ class Var(Generic[VAR_TYPE]):
662
649
 
663
650
  return boolify(self)
664
651
 
665
- def __and__(self, other: Var | Any) -> Var:
652
+ def __and__(self, other: ImmutableVar | Any) -> ImmutableVar:
666
653
  """Perform a logical AND operation on the current instance and another variable.
667
654
 
668
655
  Args:
@@ -673,7 +660,7 @@ class Var(Generic[VAR_TYPE]):
673
660
  """
674
661
  return and_operation(self, other)
675
662
 
676
- def __rand__(self, other: Var | Any) -> Var:
663
+ def __rand__(self, other: ImmutableVar | Any) -> ImmutableVar:
677
664
  """Perform a logical AND operation on the current instance and another variable.
678
665
 
679
666
  Args:
@@ -684,7 +671,7 @@ class Var(Generic[VAR_TYPE]):
684
671
  """
685
672
  return and_operation(other, self)
686
673
 
687
- def __or__(self, other: Var | Any) -> Var:
674
+ def __or__(self, other: ImmutableVar | Any) -> ImmutableVar:
688
675
  """Perform a logical OR operation on the current instance and another variable.
689
676
 
690
677
  Args:
@@ -695,7 +682,7 @@ class Var(Generic[VAR_TYPE]):
695
682
  """
696
683
  return or_operation(self, other)
697
684
 
698
- def __ror__(self, other: Var | Any) -> Var:
685
+ def __ror__(self, other: ImmutableVar | Any) -> ImmutableVar:
699
686
  """Perform a logical OR operation on the current instance and another variable.
700
687
 
701
688
  Args:
@@ -725,7 +712,7 @@ class Var(Generic[VAR_TYPE]):
725
712
 
726
713
  return JSON_STRINGIFY.call(self).to(StringVar)
727
714
 
728
- def as_ref(self) -> Var:
715
+ def as_ref(self) -> ImmutableVar:
729
716
  """Get a reference to the var.
730
717
 
731
718
  Returns:
@@ -733,14 +720,14 @@ class Var(Generic[VAR_TYPE]):
733
720
  """
734
721
  from .object import ObjectVar
735
722
 
736
- refs = Var(
737
- _js_expr="refs",
723
+ refs = ImmutableVar(
724
+ _var_name="refs",
738
725
  _var_data=VarData(
739
726
  imports={
740
727
  f"/{constants.Dirs.STATE_PATH}": [imports.ImportVar(tag="refs")]
741
728
  }
742
729
  ),
743
- ).to(ObjectVar, Dict[str, str])
730
+ ).to(ObjectVar)
744
731
  return refs[LiteralVar.create(str(self))]
745
732
 
746
733
  @deprecated("Use `.js_type()` instead.")
@@ -826,8 +813,7 @@ class Var(Generic[VAR_TYPE]):
826
813
  TypeError: If the var type is Any.
827
814
  """
828
815
  if name.startswith("_"):
829
- return self.__getattribute__(name)
830
-
816
+ return super(ImmutableVar, self).__getattribute__(name)
831
817
  if self._var_type is Any:
832
818
  raise TypeError(
833
819
  f"You must provide an annotation for the state var `{str(self)}`. Annotation cannot be `{self._var_type}`."
@@ -871,85 +857,26 @@ class Var(Generic[VAR_TYPE]):
871
857
  var_data = self._get_all_var_data()
872
858
  return var_data.state if var_data else ""
873
859
 
874
- @overload
875
- @classmethod
876
- def range(cls, stop: int | NumberVar, /) -> ArrayVar[List[int]]: ...
877
-
878
- @overload
879
- @classmethod
880
- def range(
881
- cls,
882
- start: int | NumberVar,
883
- end: int | NumberVar,
884
- step: int | NumberVar = 1,
885
- /,
886
- ) -> ArrayVar[List[int]]: ...
887
-
888
- @classmethod
889
- def range(
890
- cls,
891
- first_endpoint: int | NumberVar,
892
- second_endpoint: int | NumberVar | None = None,
893
- step: int | NumberVar | None = None,
894
- ) -> ArrayVar[List[int]]:
895
- """Create a range of numbers.
896
860
 
897
- Args:
898
- first_endpoint: The end of the range if second_endpoint is not provided, otherwise the start of the range.
899
- second_endpoint: The end of the range.
900
- step: The step of the range.
861
+ OUTPUT = TypeVar("OUTPUT", bound=ImmutableVar)
901
862
 
902
- Returns:
903
- The range of numbers.
904
- """
905
- from .sequence import ArrayVar
906
863
 
907
- return ArrayVar.range(first_endpoint, second_endpoint, step)
864
+ def _encode_var(value: ImmutableVar) -> str:
865
+ """Encode the state name into a formatted var.
908
866
 
909
- def __bool__(self) -> bool:
910
- """Raise exception if using Var in a boolean context.
911
-
912
- Raises:
913
- VarTypeError: when attempting to bool-ify the Var.
914
- """
915
- raise VarTypeError(
916
- f"Cannot convert Var {str(self)!r} to bool for use with `if`, `and`, `or`, and `not`. "
917
- "Instead use `rx.cond` and bitwise operators `&` (and), `|` (or), `~` (invert)."
918
- )
919
-
920
- def __iter__(self) -> Any:
921
- """Raise exception if using Var in an iterable context.
922
-
923
- Raises:
924
- VarTypeError: when attempting to iterate over the Var.
925
- """
926
- raise VarTypeError(
927
- f"Cannot iterate over Var {str(self)!r}. Instead use `rx.foreach`."
928
- )
929
-
930
- def __contains__(self, _: Any) -> Var:
931
- """Override the 'in' operator to alert the user that it is not supported.
932
-
933
- Raises:
934
- VarTypeError: the operation is not supported
935
- """
936
- raise VarTypeError(
937
- "'in' operator not supported for Var types, use Var.contains() instead."
938
- )
939
-
940
- def json(self) -> str:
941
- """Serialize the var to a JSON string.
867
+ Args:
868
+ value: The value to encode the state name into.
942
869
 
943
- Raises:
944
- NotImplementedError: If the method is not implemented.
945
- """
946
- raise NotImplementedError("Var subclasses must implement the json method.")
870
+ Returns:
871
+ The encoded var.
872
+ """
873
+ return f"{value}"
947
874
 
948
875
 
949
- OUTPUT = TypeVar("OUTPUT", bound=Var)
876
+ serializers.serializer(_encode_var)
950
877
 
951
878
 
952
- class LiteralVar(Var):
879
+ class LiteralVar(ImmutableVar):
953
880
  """Base class for immutable literal vars."""
954
881
 
955
882
  @classmethod
@@ -957,7 +884,7 @@ class LiteralVar(Var):
957
884
  cls,
958
885
  value: Any,
959
886
  _var_data: VarData | None = None,
960
- ) -> Var:
887
+ ) -> ImmutableVar:
961
888
  """Create a var from a value.
962
889
 
963
890
  Args:
@@ -974,7 +901,7 @@ class LiteralVar(Var):
974
901
  from .object import LiteralObjectVar
975
902
  from .sequence import LiteralArrayVar, LiteralStringVar
976
903
 
977
- if isinstance(value, Var):
904
+ if isinstance(value, ImmutableVar):
978
905
  if _var_data is None:
979
906
  return value
980
907
  return value._replace(merge_var_data=_var_data)
@@ -997,7 +924,7 @@ class LiteralVar(Var):
997
924
  if value is None:
998
925
  return LiteralNoneVar.create(_var_data=_var_data)
999
926
 
1000
- from reflex.event import EventChain, EventHandler, EventSpec
927
+ from reflex.event import EventChain, EventSpec
1001
928
  from reflex.utils.format import get_event_handler_parts
1002
929
 
1003
930
  from .function import ArgsFunctionOperation, FunctionStringVar
@@ -1021,12 +948,14 @@ class LiteralVar(Var):
1021
948
  sig = inspect.signature(value.args_spec) # type: ignore
1022
949
  if sig.parameters:
1023
950
  arg_def = tuple((f"_{p}" for p in sig.parameters))
1024
- arg_def_expr = LiteralVar.create([Var(_js_expr=arg) for arg in arg_def])
951
+ arg_def_expr = LiteralVar.create(
952
+ [ImmutableVar.create_safe(arg) for arg in arg_def]
953
+ )
1025
954
  else:
1026
955
  # add a default argument for addEvents if none were specified in value.args_spec
1027
956
  # used to trigger the preventDefault() on the event.
1028
957
  arg_def = ("...args",)
1029
- arg_def_expr = Var(_js_expr="args")
958
+ arg_def_expr = ImmutableVar.create_safe("args")
1030
959
 
1031
960
  return ArgsFunctionOperation.create(
1032
961
  arg_def,
@@ -1039,8 +968,12 @@ class LiteralVar(Var):
1039
968
  ),
1040
969
  )
1041
970
 
1042
- if isinstance(value, EventHandler):
1043
- return Var(_js_expr=".".join(filter(None, get_event_handler_parts(value))))
971
+ if isinstance(value, Base):
972
+ return LiteralObjectVar.create(
973
+ {k: (None if callable(v) else v) for k, v in value.dict().items()},
974
+ _var_type=type(value),
975
+ _var_data=_var_data,
976
+ )
1044
977
 
1045
978
  serialized_value = serializers.serialize(value)
1046
979
  if serialized_value is not None:
@@ -1050,37 +983,8 @@ class LiteralVar(Var):
1050
983
  _var_type=type(value),
1051
984
  _var_data=_var_data,
1052
985
  )
1053
- if isinstance(serialized_value, str):
1054
- return LiteralStringVar.create(
1055
- serialized_value, _var_type=type(value), _var_data=_var_data
1056
- )
1057
986
  return LiteralVar.create(serialized_value, _var_data=_var_data)
1058
987
 
1059
- if isinstance(value, Base):
1060
- # get the fields of the pydantic class
1061
- fields = value.__fields__.keys()
1062
- one_level_dict = {field: getattr(value, field) for field in fields}
1063
-
1064
- return LiteralObjectVar.create(
1065
- {
1066
- field: value
1067
- for field, value in one_level_dict.items()
1068
- if not callable(value)
1069
- },
1070
- _var_type=type(value),
1071
- _var_data=_var_data,
1072
- )
1073
-
1074
- if dataclasses.is_dataclass(value) and not isinstance(value, type):
1075
- return LiteralObjectVar.create(
1076
- {
1077
- k: (None if callable(v) else v)
1078
- for k, v in dataclasses.asdict(value).items()
1079
- },
1080
- _var_type=type(value),
1081
- _var_data=_var_data,
1082
- )
1083
-
1084
988
  raise TypeError(
1085
989
  f"Unsupported type {type(value)} for LiteralVar. Tried to create a LiteralVar from {value}."
1086
990
  )
@@ -1099,19 +1003,6 @@ class LiteralVar(Var):
1099
1003
  )
1100
1004
 
1101
1005
 
1102
- @serializers.serializer
1103
- def serialize_literal(value: LiteralVar):
1104
- """Serialize a Literal type.
1105
-
1106
- Args:
1107
- value: The Literal to serialize.
1108
-
1109
- Returns:
1110
- The serialized Literal.
1111
- """
1112
- return serializers.serialize(value._var_value)
1113
-
1114
-
1115
1006
  P = ParamSpec("P")
1116
1007
  T = TypeVar("T")
1117
1008
 
@@ -1120,7 +1011,7 @@ T = TypeVar("T")
1120
1011
  @overload
1121
1012
  def var_operation(
1122
1013
  func: Callable[P, CustomVarOperationReturn[NoReturn]],
1123
- ) -> Callable[P, Var]: ...
1014
+ ) -> Callable[P, ImmutableVar]: ...
1124
1015
 
1125
1016
 
1126
1017
  @overload
@@ -1164,7 +1055,7 @@ def var_operation(
1164
1055
 
1165
1056
  def var_operation(
1166
1057
  func: Callable[P, CustomVarOperationReturn[T]],
1167
- ) -> Callable[P, Var[T]]:
1058
+ ) -> Callable[P, ImmutableVar[T]]:
1168
1059
  """Decorator for creating a var operation.
1169
1060
 
1170
1061
  Example:
@@ -1182,14 +1073,18 @@ def var_operation(
1182
1073
  """
1183
1074
 
1184
1075
  @functools.wraps(func)
1185
- def wrapper(*args: P.args, **kwargs: P.kwargs) -> Var[T]:
1076
+ def wrapper(*args: P.args, **kwargs: P.kwargs) -> ImmutableVar[T]:
1186
1077
  func_args = list(inspect.signature(func).parameters)
1187
1078
  args_vars = {
1188
- func_args[i]: (LiteralVar.create(arg) if not isinstance(arg, Var) else arg)
1079
+ func_args[i]: (
1080
+ LiteralVar.create(arg) if not isinstance(arg, ImmutableVar) else arg
1081
+ )
1189
1082
  for i, arg in enumerate(args)
1190
1083
  }
1191
1084
  kwargs_vars = {
1192
- key: LiteralVar.create(value) if not isinstance(value, Var) else value
1085
+ key: LiteralVar.create(value)
1086
+ if not isinstance(value, ImmutableVar)
1087
+ else value
1193
1088
  for key, value in kwargs.items()
1194
1089
  }
1195
1090
 
@@ -1212,13 +1107,10 @@ def unionize(*args: Type) -> Type:
1212
1107
  """
1213
1108
  if not args:
1214
1109
  return Any
1215
- if len(args) == 1:
1216
- return args[0]
1217
- # We are bisecting the args list here to avoid hitting the recursion limit
1218
- # In Python versions >= 3.11, we can simply do `return Union[*args]`
1219
- midpoint = len(args) // 2
1220
- first_half, second_half = args[:midpoint], args[midpoint:]
1221
- return Union[unionize(*first_half), unionize(*second_half)]
1110
+ first, *rest = args
1111
+ if not rest:
1112
+ return first
1113
+ return Union[first, unionize(*rest)]
1222
1114
 
1223
1115
 
1224
1116
  def figure_out_type(value: Any) -> types.GenericType:
@@ -1241,7 +1133,7 @@ def figure_out_type(value: Any) -> types.GenericType:
1241
1133
  unionize(*(figure_out_type(k) for k in value)),
1242
1134
  unionize(*(figure_out_type(v) for v in value.values())),
1243
1135
  ]
1244
- if isinstance(value, Var):
1136
+ if isinstance(value, ImmutableVar):
1245
1137
  return value._var_type
1246
1138
  return type(value)
1247
1139
 
@@ -1264,7 +1156,7 @@ class CachedVarOperation:
1264
1156
 
1265
1157
  def __post_init__(self):
1266
1158
  """Post-initialize the CachedVarOperation."""
1267
- object.__delattr__(self, "_js_expr")
1159
+ object.__delattr__(self, "_var_name")
1268
1160
 
1269
1161
  def __getattr__(self, name: str) -> Any:
1270
1162
  """Get an attribute of the var.
@@ -1275,7 +1167,7 @@ class CachedVarOperation:
1275
1167
  Returns:
1276
1168
  The attribute.
1277
1169
  """
1278
- if name == "_js_expr":
1170
+ if name == "_var_name":
1279
1171
  return self._cached_var_name
1280
1172
 
1281
1173
  parent_classes = inspect.getmro(self.__class__)
@@ -1302,7 +1194,9 @@ class CachedVarOperation:
1302
1194
  return VarData.merge(
1303
1195
  *map(
1304
1196
  lambda value: (
1305
- value._get_all_var_data() if isinstance(value, Var) else None
1197
+ value._get_all_var_data()
1198
+ if isinstance(value, ImmutableVar)
1199
+ else None
1306
1200
  ),
1307
1201
  map(
1308
1202
  lambda field: getattr(self, field.name),
@@ -1324,13 +1218,13 @@ class CachedVarOperation:
1324
1218
  *[
1325
1219
  getattr(self, field.name)
1326
1220
  for field in dataclasses.fields(self) # type: ignore
1327
- if field.name not in ["_js_expr", "_var_data", "_var_type"]
1221
+ if field.name not in ["_var_name", "_var_data", "_var_type"]
1328
1222
  ],
1329
1223
  )
1330
1224
  )
1331
1225
 
1332
1226
 
1333
- def and_operation(a: Var | Any, b: Var | Any) -> Var:
1227
+ def and_operation(a: ImmutableVar | Any, b: ImmutableVar | Any) -> ImmutableVar:
1334
1228
  """Perform a logical AND operation on two variables.
1335
1229
 
1336
1230
  Args:
@@ -1344,7 +1238,7 @@ def and_operation(a: Var | Any, b: Var | Any) -> Var:
1344
1238
 
1345
1239
 
1346
1240
  @var_operation
1347
- def _and_operation(a: Var, b: Var):
1241
+ def _and_operation(a: ImmutableVar, b: ImmutableVar):
1348
1242
  """Perform a logical AND operation on two variables.
1349
1243
 
1350
1244
  Args:
@@ -1360,7 +1254,7 @@ def _and_operation(a: Var, b: Var):
1360
1254
  )
1361
1255
 
1362
1256
 
1363
- def or_operation(a: Var | Any, b: Var | Any) -> Var:
1257
+ def or_operation(a: ImmutableVar | Any, b: ImmutableVar | Any) -> ImmutableVar:
1364
1258
  """Perform a logical OR operation on two variables.
1365
1259
 
1366
1260
  Args:
@@ -1374,7 +1268,7 @@ def or_operation(a: Var | Any, b: Var | Any) -> Var:
1374
1268
 
1375
1269
 
1376
1270
  @var_operation
1377
- def _or_operation(a: Var, b: Var):
1271
+ def _or_operation(a: ImmutableVar, b: ImmutableVar):
1378
1272
  """Perform a logical OR operation on two variables.
1379
1273
 
1380
1274
  Args:
@@ -1395,36 +1289,36 @@ def _or_operation(a: Var, b: Var):
1395
1289
  frozen=True,
1396
1290
  **{"slots": True} if sys.version_info >= (3, 10) else {},
1397
1291
  )
1398
- class CallableVar(Var):
1292
+ class ImmutableCallableVar(ImmutableVar):
1399
1293
  """Decorate a Var-returning function to act as both a Var and a function.
1400
1294
 
1401
1295
  This is used as a compatibility shim for replacing Var objects in the
1402
1296
  API with functions that return a family of Var.
1403
1297
  """
1404
1298
 
1405
- fn: Callable[..., Var] = dataclasses.field(
1406
- default_factory=lambda: lambda: Var(_js_expr="undefined")
1299
+ fn: Callable[..., ImmutableVar] = dataclasses.field(
1300
+ default_factory=lambda: lambda: ImmutableVar(_var_name="undefined")
1407
1301
  )
1408
- original_var: Var = dataclasses.field(
1409
- default_factory=lambda: Var(_js_expr="undefined")
1302
+ original_var: ImmutableVar = dataclasses.field(
1303
+ default_factory=lambda: ImmutableVar(_var_name="undefined")
1410
1304
  )
1411
1305
 
1412
- def __init__(self, fn: Callable[..., Var]):
1306
+ def __init__(self, fn: Callable[..., ImmutableVar]):
1413
1307
  """Initialize a CallableVar.
1414
1308
 
1415
1309
  Args:
1416
1310
  fn: The function to decorate (must return Var)
1417
1311
  """
1418
1312
  original_var = fn()
1419
- super(CallableVar, self).__init__(
1420
- _js_expr=original_var._js_expr,
1313
+ super(ImmutableCallableVar, self).__init__(
1314
+ _var_name=original_var._var_name,
1421
1315
  _var_type=original_var._var_type,
1422
1316
  _var_data=VarData.merge(original_var._get_all_var_data()),
1423
1317
  )
1424
1318
  object.__setattr__(self, "fn", fn)
1425
1319
  object.__setattr__(self, "original_var", original_var)
1426
1320
 
1427
- def __call__(self, *args, **kwargs) -> Var:
1321
+ def __call__(self, *args, **kwargs) -> ImmutableVar:
1428
1322
  """Call the decorated function.
1429
1323
 
1430
1324
  Args:
@@ -1453,13 +1347,13 @@ DICT_VAL = TypeVar("DICT_VAL")
1453
1347
  LIST_INSIDE = TypeVar("LIST_INSIDE")
1454
1348
 
1455
1349
 
1456
- class FakeComputedVarBaseClass(property):
1350
+ class FakeComputedVarBaseClass(Var, property):
1457
1351
  """A fake base class for ComputedVar to avoid inheriting from property."""
1458
1352
 
1459
1353
  __pydantic_run_validation__ = False
1460
1354
 
1461
1355
 
1462
- def is_computed_var(obj: Any) -> TypeGuard[ComputedVar]:
1356
+ def is_computed_var(obj: Any) -> TypeGuard[ImmutableComputedVar]:
1463
1357
  """Check if the object is a ComputedVar.
1464
1358
 
1465
1359
  Args:
@@ -1476,7 +1370,7 @@ def is_computed_var(obj: Any) -> TypeGuard[ComputedVar]:
1476
1370
  frozen=True,
1477
1371
  **{"slots": True} if sys.version_info >= (3, 10) else {},
1478
1372
  )
1479
- class ComputedVar(Var[RETURN_TYPE]):
1373
+ class ImmutableComputedVar(ImmutableVar[RETURN_TYPE]):
1480
1374
  """A field with computed getters."""
1481
1375
 
1482
1376
  # Whether to track dependencies and cache computed values
@@ -1506,7 +1400,7 @@ class ComputedVar(Var[RETURN_TYPE]):
1506
1400
  fget: Callable[[BASE_STATE], RETURN_TYPE],
1507
1401
  initial_value: RETURN_TYPE | types.Unset = types.Unset(),
1508
1402
  cache: bool = False,
1509
- deps: Optional[List[Union[str, Var]]] = None,
1403
+ deps: Optional[List[Union[str, ImmutableVar]]] = None,
1510
1404
  auto_deps: bool = True,
1511
1405
  interval: Optional[Union[int, datetime.timedelta]] = None,
1512
1406
  backend: bool | None = None,
@@ -1530,12 +1424,12 @@ class ComputedVar(Var[RETURN_TYPE]):
1530
1424
  hints = get_type_hints(fget)
1531
1425
  hint = hints.get("return", Any)
1532
1426
 
1533
- kwargs["_js_expr"] = kwargs.pop("_js_expr", fget.__name__)
1427
+ kwargs["_var_name"] = kwargs.pop("_var_name", fget.__name__)
1534
1428
  kwargs["_var_type"] = kwargs.pop("_var_type", hint)
1535
1429
 
1536
- Var.__init__(
1430
+ ImmutableVar.__init__(
1537
1431
  self,
1538
- _js_expr=kwargs.pop("_js_expr"),
1432
+ _var_name=kwargs.pop("_var_name"),
1539
1433
  _var_type=kwargs.pop("_var_type"),
1540
1434
  _var_data=kwargs.pop("_var_data", None),
1541
1435
  )
@@ -1556,7 +1450,7 @@ class ComputedVar(Var[RETURN_TYPE]):
1556
1450
  deps = []
1557
1451
  else:
1558
1452
  for dep in deps:
1559
- if isinstance(dep, Var):
1453
+ if isinstance(dep, ImmutableVar):
1560
1454
  continue
1561
1455
  if isinstance(dep, str) and dep != "":
1562
1456
  continue
@@ -1566,7 +1460,7 @@ class ComputedVar(Var[RETURN_TYPE]):
1566
1460
  object.__setattr__(
1567
1461
  self,
1568
1462
  "_static_deps",
1569
- {dep._js_expr if isinstance(dep, Var) else dep for dep in deps},
1463
+ {dep._var_name if isinstance(dep, ImmutableVar) else dep for dep in deps},
1570
1464
  )
1571
1465
  object.__setattr__(self, "_auto_deps", auto_deps)
1572
1466
 
@@ -1594,7 +1488,7 @@ class ComputedVar(Var[RETURN_TYPE]):
1594
1488
  auto_deps=kwargs.pop("auto_deps", self._auto_deps),
1595
1489
  interval=kwargs.pop("interval", self._update_interval),
1596
1490
  backend=kwargs.pop("backend", self._backend),
1597
- _js_expr=kwargs.pop("_js_expr", self._js_expr),
1491
+ _var_name=kwargs.pop("_var_name", self._var_name),
1598
1492
  _var_type=kwargs.pop("_var_type", self._var_type),
1599
1493
  _var_data=kwargs.pop(
1600
1494
  "_var_data", VarData.merge(self._var_data, merge_var_data)
@@ -1614,7 +1508,7 @@ class ComputedVar(Var[RETURN_TYPE]):
1614
1508
  Returns:
1615
1509
  An attribute name.
1616
1510
  """
1617
- return f"__cached_{self._js_expr}"
1511
+ return f"__cached_{self._var_name}"
1618
1512
 
1619
1513
  @property
1620
1514
  def _last_updated_attr(self) -> str:
@@ -1623,7 +1517,7 @@ class ComputedVar(Var[RETURN_TYPE]):
1623
1517
  Returns:
1624
1518
  An attribute name.
1625
1519
  """
1626
- return f"__last_updated_{self._js_expr}"
1520
+ return f"__last_updated_{self._var_name}"
1627
1521
 
1628
1522
  def needs_update(self, instance: BaseState) -> bool:
1629
1523
  """Check if the computed var needs to be updated.
@@ -1643,48 +1537,50 @@ class ComputedVar(Var[RETURN_TYPE]):
1643
1537
 
1644
1538
  @overload
1645
1539
  def __get__(
1646
- self: ComputedVar[int] | ComputedVar[float],
1540
+ self: ImmutableComputedVar[int] | ImmutableComputedVar[float],
1647
1541
  instance: None,
1648
1542
  owner: Type,
1649
1543
  ) -> NumberVar: ...
1650
1544
 
1651
1545
  @overload
1652
1546
  def __get__(
1653
- self: ComputedVar[str],
1547
+ self: ImmutableComputedVar[str],
1654
1548
  instance: None,
1655
1549
  owner: Type,
1656
1550
  ) -> StringVar: ...
1657
1551
 
1658
1552
  @overload
1659
1553
  def __get__(
1660
- self: ComputedVar[dict[DICT_KEY, DICT_VAL]],
1554
+ self: ImmutableComputedVar[dict[DICT_KEY, DICT_VAL]],
1661
1555
  instance: None,
1662
1556
  owner: Type,
1663
1557
  ) -> ObjectVar[dict[DICT_KEY, DICT_VAL]]: ...
1664
1558
 
1665
1559
  @overload
1666
1560
  def __get__(
1667
- self: ComputedVar[list[LIST_INSIDE]],
1561
+ self: ImmutableComputedVar[list[LIST_INSIDE]],
1668
1562
  instance: None,
1669
1563
  owner: Type,
1670
1564
  ) -> ArrayVar[list[LIST_INSIDE]]: ...
1671
1565
 
1672
1566
  @overload
1673
1567
  def __get__(
1674
- self: ComputedVar[set[LIST_INSIDE]],
1568
+ self: ImmutableComputedVar[set[LIST_INSIDE]],
1675
1569
  instance: None,
1676
1570
  owner: Type,
1677
1571
  ) -> ArrayVar[set[LIST_INSIDE]]: ...
1678
1572
 
1679
1573
  @overload
1680
1574
  def __get__(
1681
- self: ComputedVar[tuple[LIST_INSIDE, ...]],
1575
+ self: ImmutableComputedVar[tuple[LIST_INSIDE, ...]],
1682
1576
  instance: None,
1683
1577
  owner: Type,
1684
1578
  ) -> ArrayVar[tuple[LIST_INSIDE, ...]]: ...
1685
1579
 
1686
1580
  @overload
1687
- def __get__(self, instance: None, owner: Type) -> ComputedVar[RETURN_TYPE]: ...
1581
+ def __get__(
1582
+ self, instance: None, owner: Type
1583
+ ) -> ImmutableComputedVar[RETURN_TYPE]: ...
1688
1584
 
1689
1585
  @overload
1690
1586
  def __get__(self, instance: BaseState, owner: Type) -> RETURN_TYPE: ...
@@ -1703,13 +1599,13 @@ class ComputedVar(Var[RETURN_TYPE]):
1703
1599
  """
1704
1600
  if instance is None:
1705
1601
  state_where_defined = owner
1706
- while self._js_expr in state_where_defined.inherited_vars:
1602
+ while self.fget.__name__ in state_where_defined.inherited_vars:
1707
1603
  state_where_defined = state_where_defined.get_parent_state()
1708
1604
 
1709
1605
  return self._replace(
1710
- _js_expr=format_state_name(state_where_defined.get_full_name())
1606
+ _var_name=format_state_name(state_where_defined.get_full_name())
1711
1607
  + "."
1712
- + self._js_expr,
1608
+ + self._var_name,
1713
1609
  merge_var_data=VarData.from_state(state_where_defined),
1714
1610
  ).guess_type()
1715
1611
 
@@ -1809,7 +1705,7 @@ class ComputedVar(Var[RETURN_TYPE]):
1809
1705
  )
1810
1706
  # recurse into property fget functions
1811
1707
  elif isinstance(ref_obj, property) and not isinstance(
1812
- ref_obj, ComputedVar
1708
+ ref_obj, ImmutableComputedVar
1813
1709
  ):
1814
1710
  d.update(
1815
1711
  self._deps(
@@ -1877,7 +1773,7 @@ class ComputedVar(Var[RETURN_TYPE]):
1877
1773
  return self._fget
1878
1774
 
1879
1775
 
1880
- class DynamicRouteVar(ComputedVar[Union[str, List[str]]]):
1776
+ class DynamicRouteVar(ImmutableComputedVar[Union[str, List[str]]]):
1881
1777
  """A ComputedVar that represents a dynamic route."""
1882
1778
 
1883
1779
  pass
@@ -1888,41 +1784,45 @@ if TYPE_CHECKING:
1888
1784
 
1889
1785
 
1890
1786
  @overload
1891
- def computed_var(
1787
+ def immutable_computed_var(
1892
1788
  fget: None = None,
1893
1789
  initial_value: Any | types.Unset = types.Unset(),
1894
1790
  cache: bool = False,
1895
- deps: Optional[List[Union[str, Var]]] = None,
1791
+ deps: Optional[List[Union[str, ImmutableVar]]] = None,
1896
1792
  auto_deps: bool = True,
1897
1793
  interval: Optional[Union[datetime.timedelta, int]] = None,
1898
1794
  backend: bool | None = None,
1899
1795
  **kwargs,
1900
- ) -> Callable[[Callable[[BASE_STATE], RETURN_TYPE]], ComputedVar[RETURN_TYPE]]: ...
1796
+ ) -> Callable[
1797
+ [Callable[[BASE_STATE], RETURN_TYPE]], ImmutableComputedVar[RETURN_TYPE]
1798
+ ]: ...
1901
1799
 
1902
1800
 
1903
1801
  @overload
1904
- def computed_var(
1802
+ def immutable_computed_var(
1905
1803
  fget: Callable[[BASE_STATE], RETURN_TYPE],
1906
1804
  initial_value: RETURN_TYPE | types.Unset = types.Unset(),
1907
1805
  cache: bool = False,
1908
- deps: Optional[List[Union[str, Var]]] = None,
1806
+ deps: Optional[List[Union[str, ImmutableVar]]] = None,
1909
1807
  auto_deps: bool = True,
1910
1808
  interval: Optional[Union[datetime.timedelta, int]] = None,
1911
1809
  backend: bool | None = None,
1912
1810
  **kwargs,
1913
- ) -> ComputedVar[RETURN_TYPE]: ...
1811
+ ) -> ImmutableComputedVar[RETURN_TYPE]: ...
1914
1812
 
1915
1813
 
1916
- def computed_var(
1814
+ def immutable_computed_var(
1917
1815
  fget: Callable[[BASE_STATE], Any] | None = None,
1918
1816
  initial_value: Any | types.Unset = types.Unset(),
1919
1817
  cache: bool = False,
1920
- deps: Optional[List[Union[str, Var]]] = None,
1818
+ deps: Optional[List[Union[str, ImmutableVar]]] = None,
1921
1819
  auto_deps: bool = True,
1922
1820
  interval: Optional[Union[datetime.timedelta, int]] = None,
1923
1821
  backend: bool | None = None,
1924
1822
  **kwargs,
1925
- ) -> ComputedVar | Callable[[Callable[[BASE_STATE], Any]], ComputedVar]:
1823
+ ) -> (
1824
+ ImmutableComputedVar | Callable[[Callable[[BASE_STATE], Any]], ImmutableComputedVar]
1825
+ ):
1926
1826
  """A ComputedVar decorator with or without kwargs.
1927
1827
 
1928
1828
  Args:
@@ -1949,10 +1849,10 @@ def computed_var(
1949
1849
  raise VarDependencyError("Cannot track dependencies without caching.")
1950
1850
 
1951
1851
  if fget is not None:
1952
- return ComputedVar(fget, cache=cache)
1852
+ return ImmutableComputedVar(fget, cache=cache)
1953
1853
 
1954
- def wrapper(fget: Callable[[BASE_STATE], Any]) -> ComputedVar:
1955
- return ComputedVar(
1854
+ def wrapper(fget: Callable[[BASE_STATE], Any]) -> ImmutableComputedVar:
1855
+ return ImmutableComputedVar(
1956
1856
  fget,
1957
1857
  initial_value=initial_value,
1958
1858
  cache=cache,
@@ -1969,7 +1869,7 @@ def computed_var(
1969
1869
  RETURN = TypeVar("RETURN")
1970
1870
 
1971
1871
 
1972
- class CustomVarOperationReturn(Var[RETURN]):
1872
+ class CustomVarOperationReturn(ImmutableVar[RETURN]):
1973
1873
  """Base class for custom var operations."""
1974
1874
 
1975
1875
  @classmethod
@@ -1990,7 +1890,7 @@ class CustomVarOperationReturn(Var[RETURN]):
1990
1890
  The CustomVarOperation.
1991
1891
  """
1992
1892
  return CustomVarOperationReturn(
1993
- _js_expr=js_expression,
1893
+ _var_name=js_expression,
1994
1894
  _var_type=_var_type or Any,
1995
1895
  _var_data=_var_data,
1996
1896
  )
@@ -1999,23 +1899,17 @@ class CustomVarOperationReturn(Var[RETURN]):
1999
1899
  def var_operation_return(
2000
1900
  js_expression: str,
2001
1901
  var_type: Type[RETURN] | None = None,
2002
- var_data: VarData | None = None,
2003
1902
  ) -> CustomVarOperationReturn[RETURN]:
2004
1903
  """Shortcut for creating a CustomVarOperationReturn.
2005
1904
 
2006
1905
  Args:
2007
1906
  js_expression: The JavaScript expression to evaluate.
2008
1907
  var_type: The type of the var.
2009
- var_data: Additional hooks and imports associated with the Var.
2010
1908
 
2011
1909
  Returns:
2012
1910
  The CustomVarOperationReturn.
2013
1911
  """
2014
- return CustomVarOperationReturn.create(
2015
- js_expression,
2016
- var_type,
2017
- var_data,
2018
- )
1912
+ return CustomVarOperationReturn.create(js_expression, var_type)
2019
1913
 
2020
1914
 
2021
1915
  @dataclasses.dataclass(
@@ -2023,10 +1917,12 @@ def var_operation_return(
2023
1917
  frozen=True,
2024
1918
  **{"slots": True} if sys.version_info >= (3, 10) else {},
2025
1919
  )
2026
- class CustomVarOperation(CachedVarOperation, Var[T]):
1920
+ class CustomVarOperation(CachedVarOperation, ImmutableVar[T]):
2027
1921
  """Base class for custom var operations."""
2028
1922
 
2029
- _args: Tuple[Tuple[str, Var], ...] = dataclasses.field(default_factory=tuple)
1923
+ _args: Tuple[Tuple[str, ImmutableVar], ...] = dataclasses.field(
1924
+ default_factory=tuple
1925
+ )
2030
1926
 
2031
1927
  _return: CustomVarOperationReturn[T] = dataclasses.field(
2032
1928
  default_factory=lambda: CustomVarOperationReturn.create("")
@@ -2060,7 +1956,7 @@ class CustomVarOperation(CachedVarOperation, Var[T]):
2060
1956
  @classmethod
2061
1957
  def create(
2062
1958
  cls,
2063
- args: Tuple[Tuple[str, Var], ...],
1959
+ args: Tuple[Tuple[str, ImmutableVar], ...],
2064
1960
  return_var: CustomVarOperationReturn[T],
2065
1961
  _var_data: VarData | None = None,
2066
1962
  ) -> CustomVarOperation[T]:
@@ -2075,7 +1971,7 @@ class CustomVarOperation(CachedVarOperation, Var[T]):
2075
1971
  The CustomVarOperation.
2076
1972
  """
2077
1973
  return CustomVarOperation(
2078
- _js_expr="",
1974
+ _var_name="",
2079
1975
  _var_type=return_var._var_type,
2080
1976
  _var_data=_var_data,
2081
1977
  _args=args,
@@ -2083,7 +1979,7 @@ class CustomVarOperation(CachedVarOperation, Var[T]):
2083
1979
  )
2084
1980
 
2085
1981
 
2086
- class NoneVar(Var[None]):
1982
+ class NoneVar(ImmutableVar[None]):
2087
1983
  """A var representing None."""
2088
1984
 
2089
1985
 
@@ -2112,7 +2008,7 @@ class LiteralNoneVar(LiteralVar, NoneVar):
2112
2008
  The var.
2113
2009
  """
2114
2010
  return LiteralNoneVar(
2115
- _js_expr="null",
2011
+ _var_name="null",
2116
2012
  _var_type=None,
2117
2013
  _var_data=_var_data,
2118
2014
  )
@@ -2155,7 +2051,7 @@ class ToNoneOperation(CachedVarOperation, NoneVar):
2155
2051
  The ToNoneOperation.
2156
2052
  """
2157
2053
  return ToNoneOperation(
2158
- _js_expr="",
2054
+ _var_name="",
2159
2055
  _var_type=None,
2160
2056
  _var_data=_var_data,
2161
2057
  _original_var=var,
@@ -2167,7 +2063,7 @@ class ToNoneOperation(CachedVarOperation, NoneVar):
2167
2063
  frozen=True,
2168
2064
  **{"slots": True} if sys.version_info >= (3, 10) else {},
2169
2065
  )
2170
- class StateOperation(CachedVarOperation, Var):
2066
+ class StateOperation(CachedVarOperation, ImmutableVar):
2171
2067
  """A var operation that accesses a field on an object."""
2172
2068
 
2173
2069
  _state_name: str = dataclasses.field(default="")
@@ -2191,7 +2087,7 @@ class StateOperation(CachedVarOperation, Var):
2191
2087
  Returns:
2192
2088
  The attribute.
2193
2089
  """
2194
- if name == "_js_expr":
2090
+ if name == "_var_name":
2195
2091
  return self._cached_var_name
2196
2092
 
2197
2093
  return getattr(self._field, name)
@@ -2200,7 +2096,7 @@ class StateOperation(CachedVarOperation, Var):
2200
2096
  def create(
2201
2097
  cls,
2202
2098
  state_name: str,
2203
- field: Var,
2099
+ field: ImmutableVar,
2204
2100
  _var_data: VarData | None = None,
2205
2101
  ) -> StateOperation:
2206
2102
  """Create a DotOperation.
@@ -2214,7 +2110,7 @@ class StateOperation(CachedVarOperation, Var):
2214
2110
  The DotOperation.
2215
2111
  """
2216
2112
  return StateOperation(
2217
- _js_expr="",
2113
+ _var_name="",
2218
2114
  _var_type=field._var_type,
2219
2115
  _var_data=_var_data,
2220
2116
  _state_name=state_name,
@@ -2238,7 +2134,7 @@ class ToOperation:
2238
2134
 
2239
2135
  def __post_init__(self):
2240
2136
  """Post initialization."""
2241
- object.__delattr__(self, "_js_expr")
2137
+ object.__delattr__(self, "_var_name")
2242
2138
 
2243
2139
  def __hash__(self) -> int:
2244
2140
  """Calculate the hash value of the object.
@@ -2277,287 +2173,8 @@ class ToOperation:
2277
2173
  The ToOperation.
2278
2174
  """
2279
2175
  return cls(
2280
- _js_expr="", # type: ignore
2176
+ _var_name="", # type: ignore
2281
2177
  _var_data=_var_data, # type: ignore
2282
2178
  _var_type=_var_type or cls._default_var_type, # type: ignore
2283
2179
  _original=value, # type: ignore
2284
2180
  )
2285
-
2286
-
2287
- def get_uuid_string_var() -> Var:
2288
- """Return a Var that generates a single memoized UUID via .web/utils/state.js.
2289
-
2290
- useMemo with an empty dependency array ensures that the generated UUID is
2291
- consistent across re-renders of the component.
2292
-
2293
- Returns:
2294
- A Var that generates a UUID at runtime.
2295
- """
2296
- from reflex.utils.imports import ImportVar
2297
- from reflex.vars import Var
2298
-
2299
- unique_uuid_var = get_unique_variable_name()
2300
- unique_uuid_var_data = VarData(
2301
- imports={
2302
- f"/{constants.Dirs.STATE_PATH}": {ImportVar(tag="generateUUID")}, # type: ignore
2303
- "react": "useMemo",
2304
- },
2305
- hooks={f"const {unique_uuid_var} = useMemo(generateUUID, [])": None},
2306
- )
2307
-
2308
- return Var(
2309
- _js_expr=unique_uuid_var,
2310
- _var_type=str,
2311
- _var_data=unique_uuid_var_data,
2312
- )
2313
-
2314
-
2315
- # Set of unique variable names.
2316
- USED_VARIABLES = set()
2317
-
2318
-
2319
- def get_unique_variable_name() -> str:
2320
- """Get a unique variable name.
2321
-
2322
- Returns:
2323
- The unique variable name.
2324
- """
2325
- name = "".join([random.choice(string.ascii_lowercase) for _ in range(8)])
2326
- if name not in USED_VARIABLES:
2327
- USED_VARIABLES.add(name)
2328
- return name
2329
- return get_unique_variable_name()
2330
-
2331
-
2332
- @dataclasses.dataclass(
2333
- eq=True,
2334
- frozen=True,
2335
- )
2336
- class VarData:
2337
- """Metadata associated with a x."""
2338
-
2339
- # The name of the enclosing state.
2340
- state: str = dataclasses.field(default="")
2341
-
2342
- # Imports needed to render this var
2343
- imports: ImmutableParsedImportDict = dataclasses.field(default_factory=tuple)
2344
-
2345
- # Hooks that need to be present in the component to render this var
2346
- hooks: Tuple[str, ...] = dataclasses.field(default_factory=tuple)
2347
-
2348
- def __init__(
2349
- self,
2350
- state: str = "",
2351
- imports: ImportDict | ParsedImportDict | None = None,
2352
- hooks: dict[str, None] | None = None,
2353
- ):
2354
- """Initialize the var data.
2355
-
2356
- Args:
2357
- state: The name of the enclosing state.
2358
- imports: Imports needed to render this var.
2359
- hooks: Hooks that need to be present in the component to render this var.
2360
- """
2361
- immutable_imports: ImmutableParsedImportDict = tuple(
2362
- sorted(
2363
- ((k, tuple(sorted(v))) for k, v in parse_imports(imports or {}).items())
2364
- )
2365
- )
2366
- object.__setattr__(self, "state", state)
2367
- object.__setattr__(self, "imports", immutable_imports)
2368
- object.__setattr__(self, "hooks", tuple(hooks or {}))
2369
-
2370
- def old_school_imports(self) -> ImportDict:
2371
- """Return the imports as a mutable dict.
2372
-
2373
- Returns:
2374
- The imports as a mutable dict.
2375
- """
2376
- return dict((k, list(v)) for k, v in self.imports)
2377
-
2378
- @classmethod
2379
- def merge(cls, *others: VarData | None) -> VarData | None:
2380
- """Merge multiple var data objects.
2381
-
2382
- Args:
2383
- *others: The var data objects to merge.
2384
-
2385
- Returns:
2386
- The merged var data object.
2387
- """
2388
- state = ""
2389
- _imports = {}
2390
- hooks = {}
2391
- for var_data in others:
2392
- if var_data is None:
2393
- continue
2394
- state = state or var_data.state
2395
- _imports = imports.merge_imports(_imports, var_data.imports)
2396
- hooks.update(
2397
- var_data.hooks
2398
- if isinstance(var_data.hooks, dict)
2399
- else {k: None for k in var_data.hooks}
2400
- )
2401
-
2402
- if state or _imports or hooks:
2403
- return VarData(
2404
- state=state,
2405
- imports=_imports,
2406
- hooks=hooks,
2407
- )
2408
- return None
2409
-
2410
- def __bool__(self) -> bool:
2411
- """Check if the var data is non-empty.
2412
-
2413
- Returns:
2414
- True if any field is set to a non-default value.
2415
- """
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
- )
2441
-
2442
- @classmethod
2443
- def from_state(cls, state: Type[BaseState] | str) -> VarData:
2444
- """Set the state of the var.
2445
-
2446
- Args:
2447
- state: The state to set or the full name of the state.
2448
-
2449
- Returns:
2450
- The var with the set state.
2451
- """
2452
- from reflex.utils import format
2453
-
2454
- state_name = state if isinstance(state, str) else state.get_full_name()
2455
- new_var_data = VarData(
2456
- state=state_name,
2457
- hooks={
2458
- "const {0} = useContext(StateContexts.{0})".format(
2459
- format.format_state_name(state_name)
2460
- ): None
2461
- },
2462
- imports={
2463
- f"/{constants.Dirs.CONTEXTS_PATH}": [ImportVar(tag="StateContexts")],
2464
- "react": [ImportVar(tag="useContext")],
2465
- },
2466
- )
2467
- return new_var_data
2468
-
2469
-
2470
- def _decode_var_immutable(value: str) -> tuple[VarData | None, str]:
2471
- """Decode the state name from a formatted var.
2472
-
2473
- Args:
2474
- value: The value to extract the state name from.
2475
-
2476
- Returns:
2477
- The extracted state name and the value without the state name.
2478
- """
2479
- var_datas = []
2480
- if isinstance(value, str):
2481
- # fast path if there is no encoded VarData
2482
- if constants.REFLEX_VAR_OPENING_TAG not in value:
2483
- return None, value
2484
-
2485
- offset = 0
2486
-
2487
- # Find all tags.
2488
- while m := _decode_var_pattern.search(value):
2489
- start, end = m.span()
2490
- value = value[:start] + value[end:]
2491
-
2492
- serialized_data = m.group(1)
2493
-
2494
- if serialized_data.isnumeric() or (
2495
- serialized_data[0] == "-" and serialized_data[1:].isnumeric()
2496
- ):
2497
- # This is a global immutable var.
2498
- var = _global_vars[int(serialized_data)]
2499
- var_data = var._get_all_var_data()
2500
-
2501
- if var_data is not None:
2502
- var_datas.append(var_data)
2503
- offset += end - start
2504
-
2505
- return VarData.merge(*var_datas) if var_datas else None, value
2506
-
2507
-
2508
- # Compile regex for finding reflex var tags.
2509
- _decode_var_pattern_re = (
2510
- rf"{constants.REFLEX_VAR_OPENING_TAG}(.*?){constants.REFLEX_VAR_CLOSING_TAG}"
2511
- )
2512
- _decode_var_pattern = re.compile(_decode_var_pattern_re, flags=re.DOTALL)
2513
-
2514
- # Defined global immutable vars.
2515
- _global_vars: Dict[int, Var] = {}
2516
-
2517
-
2518
- def _extract_var_data(value: Iterable) -> list[VarData | None]:
2519
- """Extract the var imports and hooks from an iterable containing a Var.
2520
-
2521
- Args:
2522
- value: The iterable to extract the VarData from
2523
-
2524
- Returns:
2525
- The extracted VarDatas.
2526
- """
2527
- from reflex.style import Style
2528
- from reflex.vars import Var
2529
-
2530
- var_datas = []
2531
- with contextlib.suppress(TypeError):
2532
- for sub in value:
2533
- if isinstance(sub, Var):
2534
- var_datas.append(sub._var_data)
2535
- elif not isinstance(sub, str):
2536
- # Recurse into dict values.
2537
- if hasattr(sub, "values") and callable(sub.values):
2538
- var_datas.extend(_extract_var_data(sub.values()))
2539
- # Recurse into iterable values (or dict keys).
2540
- var_datas.extend(_extract_var_data(sub))
2541
-
2542
- # Style objects should already have _var_data.
2543
- if isinstance(value, Style):
2544
- var_datas.append(value._var_data)
2545
- else:
2546
- # Recurse when value is a dict itself.
2547
- values = getattr(value, "values", None)
2548
- if callable(values):
2549
- var_datas.extend(_extract_var_data(values()))
2550
- return var_datas
2551
-
2552
-
2553
- # These names were changed in reflex 0.3.0
2554
- REPLACED_NAMES = {
2555
- "full_name": "_var_full_name",
2556
- "name": "_js_expr",
2557
- "state": "_var_data.state",
2558
- "type_": "_var_type",
2559
- "is_local": "_var_is_local",
2560
- "is_string": "_var_is_string",
2561
- "set_state": "_var_set_state",
2562
- "deps": "_deps",
2563
- }