reflex 0.5.10a3__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 (237) hide show
  1. reflex/.templates/jinja/web/pages/utils.js.jinja2 +4 -4
  2. reflex/.templates/jinja/web/utils/context.js.jinja2 +1 -1
  3. reflex/.templates/jinja/web/utils/theme.js.jinja2 +1 -1
  4. reflex/__init__.py +3 -2
  5. reflex/__init__.pyi +2 -2
  6. reflex/app.py +43 -9
  7. reflex/base.py +3 -2
  8. reflex/compiler/compiler.py +6 -6
  9. reflex/compiler/utils.py +5 -3
  10. reflex/components/base/app_wrap.py +2 -4
  11. reflex/components/base/app_wrap.pyi +17 -17
  12. reflex/components/base/bare.py +7 -4
  13. reflex/components/base/body.pyi +17 -17
  14. reflex/components/base/document.pyi +81 -81
  15. reflex/components/base/error_boundary.py +10 -8
  16. reflex/components/base/error_boundary.pyi +20 -19
  17. reflex/components/base/fragment.pyi +17 -17
  18. reflex/components/base/head.pyi +33 -33
  19. reflex/components/base/link.pyi +34 -33
  20. reflex/components/base/meta.pyi +65 -65
  21. reflex/components/base/script.py +2 -1
  22. reflex/components/base/script.pyi +21 -20
  23. reflex/components/component.py +116 -145
  24. reflex/components/core/banner.py +59 -60
  25. reflex/components/core/banner.pyi +86 -150
  26. reflex/components/core/client_side_routing.py +2 -1
  27. reflex/components/core/client_side_routing.pyi +34 -33
  28. reflex/components/core/clipboard.py +2 -2
  29. reflex/components/core/clipboard.pyi +19 -18
  30. reflex/components/core/cond.py +21 -44
  31. reflex/components/core/debounce.py +6 -8
  32. reflex/components/core/debounce.pyi +19 -18
  33. reflex/components/core/foreach.py +5 -14
  34. reflex/components/core/html.pyi +18 -17
  35. reflex/components/core/match.py +36 -43
  36. reflex/components/core/upload.py +32 -25
  37. reflex/components/core/upload.pyi +84 -73
  38. reflex/components/datadisplay/code.py +55 -28
  39. reflex/components/datadisplay/code.pyi +20 -17
  40. reflex/components/datadisplay/dataeditor.py +17 -11
  41. reflex/components/datadisplay/dataeditor.pyi +34 -33
  42. reflex/components/el/__init__.py +0 -1
  43. reflex/components/el/__init__.pyi +0 -11
  44. reflex/components/el/element.pyi +17 -17
  45. reflex/components/el/elements/__init__.py +1 -7
  46. reflex/components/el/elements/__init__.pyi +1 -15
  47. reflex/components/el/elements/base.pyi +18 -17
  48. reflex/components/el/elements/forms.py +24 -31
  49. reflex/components/el/elements/forms.pyi +237 -236
  50. reflex/components/el/elements/inline.pyi +450 -449
  51. reflex/components/el/elements/media.py +0 -21
  52. reflex/components/el/elements/media.pyi +338 -337
  53. reflex/components/el/elements/metadata.py +3 -2
  54. reflex/components/el/elements/metadata.pyi +98 -97
  55. reflex/components/el/elements/other.pyi +114 -113
  56. reflex/components/el/elements/scripts.pyi +50 -49
  57. reflex/components/el/elements/sectioning.pyi +242 -241
  58. reflex/components/el/elements/tables.pyi +162 -161
  59. reflex/components/el/elements/typography.pyi +242 -241
  60. reflex/components/gridjs/datatable.py +13 -14
  61. reflex/components/gridjs/datatable.pyi +34 -33
  62. reflex/components/lucide/icon.py +2 -126
  63. reflex/components/lucide/icon.pyi +34 -142
  64. reflex/components/markdown/markdown.py +30 -35
  65. reflex/components/markdown/markdown.pyi +29 -32
  66. reflex/components/moment/moment.pyi +19 -18
  67. reflex/components/next/base.pyi +17 -17
  68. reflex/components/next/image.py +0 -4
  69. reflex/components/next/image.pyi +20 -19
  70. reflex/components/next/link.pyi +18 -17
  71. reflex/components/next/video.pyi +18 -17
  72. reflex/components/plotly/plotly.py +16 -28
  73. reflex/components/plotly/plotly.pyi +36 -35
  74. reflex/components/props.py +21 -10
  75. reflex/components/radix/__init__.pyi +1 -1
  76. reflex/components/radix/primitives/__init__.pyi +0 -1
  77. reflex/components/radix/primitives/accordion.py +7 -8
  78. reflex/components/radix/primitives/accordion.pyi +117 -116
  79. reflex/components/radix/primitives/base.pyi +34 -33
  80. reflex/components/radix/primitives/drawer.pyi +169 -168
  81. reflex/components/radix/primitives/form.pyi +168 -167
  82. reflex/components/radix/primitives/progress.pyi +82 -81
  83. reflex/components/radix/primitives/slider.pyi +84 -83
  84. reflex/components/radix/themes/base.py +8 -4
  85. reflex/components/radix/themes/base.pyi +114 -113
  86. reflex/components/radix/themes/color_mode.py +12 -21
  87. reflex/components/radix/themes/color_mode.pyi +67 -67
  88. reflex/components/radix/themes/components/__init__.pyi +1 -0
  89. reflex/components/radix/themes/components/alert_dialog.pyi +118 -117
  90. reflex/components/radix/themes/components/aspect_ratio.pyi +18 -17
  91. reflex/components/radix/themes/components/avatar.pyi +18 -17
  92. reflex/components/radix/themes/components/badge.pyi +18 -17
  93. reflex/components/radix/themes/components/button.pyi +18 -17
  94. reflex/components/radix/themes/components/callout.pyi +82 -81
  95. reflex/components/radix/themes/components/card.pyi +18 -17
  96. reflex/components/radix/themes/components/checkbox.py +2 -3
  97. reflex/components/radix/themes/components/checkbox.pyi +53 -52
  98. reflex/components/radix/themes/components/checkbox_cards.pyi +34 -33
  99. reflex/components/radix/themes/components/checkbox_group.pyi +34 -33
  100. reflex/components/radix/themes/components/context_menu.pyi +140 -139
  101. reflex/components/radix/themes/components/data_list.py +5 -0
  102. reflex/components/radix/themes/components/data_list.pyi +71 -65
  103. reflex/components/radix/themes/components/dialog.pyi +121 -120
  104. reflex/components/radix/themes/components/dropdown_menu.pyi +142 -141
  105. reflex/components/radix/themes/components/hover_card.pyi +68 -67
  106. reflex/components/radix/themes/components/icon_button.py +2 -1
  107. reflex/components/radix/themes/components/icon_button.pyi +18 -17
  108. reflex/components/radix/themes/components/inset.pyi +18 -17
  109. reflex/components/radix/themes/components/popover.pyi +73 -72
  110. reflex/components/radix/themes/components/progress.pyi +18 -17
  111. reflex/components/radix/themes/components/radio.pyi +18 -17
  112. reflex/components/radix/themes/components/radio_cards.pyi +35 -34
  113. reflex/components/radix/themes/components/radio_group.py +35 -31
  114. reflex/components/radix/themes/components/radio_group.pyi +73 -66
  115. reflex/components/radix/themes/components/scroll_area.pyi +18 -17
  116. reflex/components/radix/themes/components/segmented_control.pyi +35 -34
  117. reflex/components/radix/themes/components/select.py +2 -1
  118. reflex/components/radix/themes/components/select.pyi +155 -154
  119. reflex/components/radix/themes/components/separator.py +2 -3
  120. reflex/components/radix/themes/components/separator.pyi +18 -17
  121. reflex/components/radix/themes/components/skeleton.pyi +18 -17
  122. reflex/components/radix/themes/components/slider.py +2 -1
  123. reflex/components/radix/themes/components/slider.pyi +20 -19
  124. reflex/components/radix/themes/components/spinner.pyi +18 -17
  125. reflex/components/radix/themes/components/switch.pyi +19 -18
  126. reflex/components/radix/themes/components/table.pyi +114 -113
  127. reflex/components/radix/themes/components/tabs.pyi +84 -83
  128. reflex/components/radix/themes/components/text_area.pyi +21 -20
  129. reflex/components/radix/themes/components/text_field.py +0 -79
  130. reflex/components/radix/themes/components/text_field.pyi +57 -63
  131. reflex/components/radix/themes/components/tooltip.pyi +21 -20
  132. reflex/components/radix/themes/layout/base.pyi +18 -17
  133. reflex/components/radix/themes/layout/box.pyi +18 -17
  134. reflex/components/radix/themes/layout/center.pyi +18 -17
  135. reflex/components/radix/themes/layout/container.py +2 -3
  136. reflex/components/radix/themes/layout/container.pyi +18 -17
  137. reflex/components/radix/themes/layout/flex.pyi +18 -17
  138. reflex/components/radix/themes/layout/grid.pyi +18 -17
  139. reflex/components/radix/themes/layout/list.py +5 -4
  140. reflex/components/radix/themes/layout/list.pyi +86 -85
  141. reflex/components/radix/themes/layout/section.py +2 -3
  142. reflex/components/radix/themes/layout/section.pyi +18 -17
  143. reflex/components/radix/themes/layout/spacer.pyi +18 -17
  144. reflex/components/radix/themes/layout/stack.pyi +50 -49
  145. reflex/components/radix/themes/typography/blockquote.pyi +18 -17
  146. reflex/components/radix/themes/typography/code.pyi +18 -17
  147. reflex/components/radix/themes/typography/heading.pyi +18 -17
  148. reflex/components/radix/themes/typography/link.pyi +18 -17
  149. reflex/components/radix/themes/typography/text.pyi +114 -113
  150. reflex/components/react_player/audio.pyi +34 -33
  151. reflex/components/react_player/react_player.pyi +34 -33
  152. reflex/components/react_player/video.pyi +34 -33
  153. reflex/components/recharts/cartesian.py +23 -19
  154. reflex/components/recharts/cartesian.pyi +297 -296
  155. reflex/components/recharts/charts.py +6 -5
  156. reflex/components/recharts/charts.pyi +179 -178
  157. reflex/components/recharts/general.py +8 -7
  158. reflex/components/recharts/general.pyi +82 -81
  159. reflex/components/recharts/polar.py +14 -13
  160. reflex/components/recharts/polar.pyi +76 -75
  161. reflex/components/recharts/recharts.pyi +33 -33
  162. reflex/components/sonner/toast.py +30 -33
  163. reflex/components/sonner/toast.pyi +27 -25
  164. reflex/components/suneditor/editor.py +2 -1
  165. reflex/components/suneditor/editor.pyi +27 -26
  166. reflex/components/tags/iter_tag.py +16 -16
  167. reflex/components/tags/tag.py +8 -10
  168. reflex/constants/base.py +3 -1
  169. reflex/constants/event.py +1 -0
  170. reflex/event.py +89 -79
  171. reflex/experimental/__init__.py +25 -6
  172. reflex/experimental/client_state.py +34 -58
  173. reflex/experimental/hooks.py +13 -18
  174. reflex/experimental/layout.py +5 -5
  175. reflex/experimental/layout.pyi +84 -83
  176. reflex/{experimental/vars → ivars}/__init__.py +0 -1
  177. reflex/ivars/base.py +2180 -0
  178. reflex/ivars/function.py +200 -0
  179. reflex/ivars/number.py +1137 -0
  180. reflex/ivars/object.py +564 -0
  181. reflex/ivars/sequence.py +1601 -0
  182. reflex/model.py +22 -0
  183. reflex/reflex.py +4 -0
  184. reflex/state.py +388 -73
  185. reflex/style.py +52 -34
  186. reflex/testing.py +8 -3
  187. reflex/utils/exceptions.py +12 -0
  188. reflex/utils/exec.py +0 -14
  189. reflex/utils/format.py +74 -223
  190. reflex/utils/net.py +43 -0
  191. reflex/utils/path_ops.py +13 -1
  192. reflex/utils/prerequisites.py +46 -26
  193. reflex/utils/pyi_generator.py +5 -4
  194. reflex/utils/serializers.py +13 -31
  195. reflex/utils/types.py +44 -9
  196. reflex/vars.py +127 -2230
  197. {reflex-0.5.10a3.dist-info → reflex-0.6.0a1.dist-info}/METADATA +4 -6
  198. reflex-0.6.0a1.dist-info/RECORD +384 -0
  199. reflex/.templates/apps/demo/.gitignore +0 -4
  200. reflex/.templates/apps/demo/assets/favicon.ico +0 -0
  201. reflex/.templates/apps/demo/assets/github.svg +0 -10
  202. reflex/.templates/apps/demo/assets/icon.svg +0 -37
  203. reflex/.templates/apps/demo/assets/logo.svg +0 -68
  204. reflex/.templates/apps/demo/assets/paneleft.svg +0 -13
  205. reflex/.templates/apps/demo/code/__init__.py +0 -1
  206. reflex/.templates/apps/demo/code/demo.py +0 -127
  207. reflex/.templates/apps/demo/code/pages/__init__.py +0 -7
  208. reflex/.templates/apps/demo/code/pages/chatapp.py +0 -31
  209. reflex/.templates/apps/demo/code/pages/datatable.py +0 -360
  210. reflex/.templates/apps/demo/code/pages/forms.py +0 -257
  211. reflex/.templates/apps/demo/code/pages/graphing.py +0 -253
  212. reflex/.templates/apps/demo/code/pages/home.py +0 -56
  213. reflex/.templates/apps/demo/code/sidebar.py +0 -178
  214. reflex/.templates/apps/demo/code/state.py +0 -22
  215. reflex/.templates/apps/demo/code/states/form_state.py +0 -40
  216. reflex/.templates/apps/demo/code/states/pie_state.py +0 -47
  217. reflex/.templates/apps/demo/code/styles.py +0 -68
  218. reflex/.templates/apps/demo/code/webui/__init__.py +0 -0
  219. reflex/.templates/apps/demo/code/webui/components/__init__.py +0 -4
  220. reflex/.templates/apps/demo/code/webui/components/chat.py +0 -118
  221. reflex/.templates/apps/demo/code/webui/components/loading_icon.py +0 -19
  222. reflex/.templates/apps/demo/code/webui/components/modal.py +0 -56
  223. reflex/.templates/apps/demo/code/webui/components/navbar.py +0 -70
  224. reflex/.templates/apps/demo/code/webui/components/sidebar.py +0 -66
  225. reflex/.templates/apps/demo/code/webui/state.py +0 -146
  226. reflex/.templates/apps/demo/code/webui/styles.py +0 -88
  227. reflex/experimental/vars/base.py +0 -583
  228. reflex/experimental/vars/function.py +0 -290
  229. reflex/experimental/vars/number.py +0 -1458
  230. reflex/experimental/vars/object.py +0 -804
  231. reflex/experimental/vars/sequence.py +0 -1764
  232. reflex/utils/watch.py +0 -96
  233. reflex/vars.pyi +0 -218
  234. reflex-0.5.10a3.dist-info/RECORD +0 -413
  235. {reflex-0.5.10a3.dist-info → reflex-0.6.0a1.dist-info}/LICENSE +0 -0
  236. {reflex-0.5.10a3.dist-info → reflex-0.6.0a1.dist-info}/WHEEL +0 -0
  237. {reflex-0.5.10a3.dist-info → reflex-0.6.0a1.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,1601 @@
1
+ """Collection of string classes and utilities."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import dataclasses
6
+ import inspect
7
+ import json
8
+ import re
9
+ import sys
10
+ import typing
11
+ from typing import (
12
+ TYPE_CHECKING,
13
+ Any,
14
+ ClassVar,
15
+ Dict,
16
+ List,
17
+ Literal,
18
+ NoReturn,
19
+ Set,
20
+ Tuple,
21
+ Type,
22
+ TypeVar,
23
+ Union,
24
+ overload,
25
+ )
26
+
27
+ from reflex import constants
28
+ from reflex.constants.base import REFLEX_VAR_OPENING_TAG
29
+ from reflex.utils.exceptions import VarTypeError
30
+ from reflex.utils.types import GenericType, get_origin
31
+ from reflex.vars import (
32
+ Var,
33
+ VarData,
34
+ _global_vars,
35
+ get_unique_variable_name,
36
+ )
37
+
38
+ from .base import (
39
+ CachedVarOperation,
40
+ CustomVarOperationReturn,
41
+ ImmutableVar,
42
+ LiteralNoneVar,
43
+ LiteralVar,
44
+ ToOperation,
45
+ cached_property_no_lock,
46
+ figure_out_type,
47
+ unionize,
48
+ var_operation,
49
+ var_operation_return,
50
+ )
51
+ from .number import (
52
+ BooleanVar,
53
+ LiteralNumberVar,
54
+ NumberVar,
55
+ raise_unsupported_operand_types,
56
+ )
57
+
58
+ if TYPE_CHECKING:
59
+ from .object import ObjectVar
60
+
61
+
62
+ class StringVar(ImmutableVar[str]):
63
+ """Base class for immutable string vars."""
64
+
65
+ @overload
66
+ def __add__(self, other: StringVar | str) -> ConcatVarOperation: ...
67
+
68
+ @overload
69
+ def __add__(self, other: NoReturn) -> NoReturn: ...
70
+
71
+ def __add__(self, other: Any) -> ConcatVarOperation:
72
+ """Concatenate two strings.
73
+
74
+ Args:
75
+ other: The other string.
76
+
77
+ Returns:
78
+ The string concatenation operation.
79
+ """
80
+ if not isinstance(other, (StringVar, str)):
81
+ raise_unsupported_operand_types("+", (type(self), type(other)))
82
+
83
+ return ConcatVarOperation.create(self, other)
84
+
85
+ @overload
86
+ def __radd__(self, other: StringVar | str) -> ConcatVarOperation: ...
87
+
88
+ @overload
89
+ def __radd__(self, other: NoReturn) -> NoReturn: ...
90
+
91
+ def __radd__(self, other: Any) -> ConcatVarOperation:
92
+ """Concatenate two strings.
93
+
94
+ Args:
95
+ other: The other string.
96
+
97
+ Returns:
98
+ The string concatenation operation.
99
+ """
100
+ if not isinstance(other, (StringVar, str)):
101
+ raise_unsupported_operand_types("+", (type(other), type(self)))
102
+
103
+ return ConcatVarOperation.create(other, self)
104
+
105
+ @overload
106
+ def __mul__(self, other: NumberVar | int) -> StringVar: ...
107
+
108
+ @overload
109
+ def __mul__(self, other: NoReturn) -> NoReturn: ...
110
+
111
+ def __mul__(self, other: Any) -> StringVar:
112
+ """Multiply the sequence by a number or an integer.
113
+
114
+ Args:
115
+ other: The number or integer to multiply the sequence by.
116
+
117
+ Returns:
118
+ StringVar: The resulting sequence after multiplication.
119
+ """
120
+ if not isinstance(other, (NumberVar, int)):
121
+ raise_unsupported_operand_types("*", (type(self), type(other)))
122
+
123
+ return (self.split() * other).join()
124
+
125
+ @overload
126
+ def __rmul__(self, other: NumberVar | int) -> StringVar: ...
127
+
128
+ @overload
129
+ def __rmul__(self, other: NoReturn) -> NoReturn: ...
130
+
131
+ def __rmul__(self, other: Any) -> StringVar:
132
+ """Multiply the sequence by a number or an integer.
133
+
134
+ Args:
135
+ other: The number or integer to multiply the sequence by.
136
+
137
+ Returns:
138
+ StringVar: The resulting sequence after multiplication.
139
+ """
140
+ if not isinstance(other, (NumberVar, int)):
141
+ raise_unsupported_operand_types("*", (type(other), type(self)))
142
+
143
+ return (self.split() * other).join()
144
+
145
+ @overload
146
+ def __getitem__(self, i: slice) -> StringVar: ...
147
+
148
+ @overload
149
+ def __getitem__(self, i: int | NumberVar) -> StringVar: ...
150
+
151
+ def __getitem__(self, i: Any) -> StringVar:
152
+ """Get a slice of the string.
153
+
154
+ Args:
155
+ i: The slice.
156
+
157
+ Returns:
158
+ The string slice operation.
159
+ """
160
+ if isinstance(i, slice):
161
+ return self.split()[i].join()
162
+ if not isinstance(i, (int, NumberVar)) or (
163
+ isinstance(i, NumberVar) and i._is_strict_float()
164
+ ):
165
+ raise_unsupported_operand_types("[]", (type(self), type(i)))
166
+ return string_item_operation(self, i)
167
+
168
+ def length(self) -> NumberVar:
169
+ """Get the length of the string.
170
+
171
+ Returns:
172
+ The string length operation.
173
+ """
174
+ return self.split().length()
175
+
176
+ def lower(self) -> StringVar:
177
+ """Convert the string to lowercase.
178
+
179
+ Returns:
180
+ The string lower operation.
181
+ """
182
+ return string_lower_operation(self)
183
+
184
+ def upper(self) -> StringVar:
185
+ """Convert the string to uppercase.
186
+
187
+ Returns:
188
+ The string upper operation.
189
+ """
190
+ return string_upper_operation(self)
191
+
192
+ def strip(self) -> StringVar:
193
+ """Strip the string.
194
+
195
+ Returns:
196
+ The string strip operation.
197
+ """
198
+ return string_strip_operation(self)
199
+
200
+ def bool(self):
201
+ """Boolean conversion.
202
+
203
+ Returns:
204
+ The boolean value of the string.
205
+ """
206
+ return self.length() != 0
207
+
208
+ def reversed(self) -> StringVar:
209
+ """Reverse the string.
210
+
211
+ Returns:
212
+ The string reverse operation.
213
+ """
214
+ return self.split().reverse().join()
215
+
216
+ @overload
217
+ def contains(
218
+ self, other: StringVar | str, field: StringVar | str | None = None
219
+ ) -> BooleanVar: ...
220
+
221
+ @overload
222
+ def contains(
223
+ self, other: NoReturn, field: StringVar | str | None = None
224
+ ) -> NoReturn: ...
225
+
226
+ def contains(self, other: Any, field: Any = None) -> BooleanVar:
227
+ """Check if the string contains another string.
228
+
229
+ Args:
230
+ other: The other string.
231
+ field: The field to check.
232
+
233
+ Returns:
234
+ The string contains operation.
235
+ """
236
+ if not isinstance(other, (StringVar, str)):
237
+ raise_unsupported_operand_types("contains", (type(self), type(other)))
238
+ if field is not None:
239
+ if not isinstance(field, (StringVar, str)):
240
+ raise_unsupported_operand_types("contains", (type(self), type(field)))
241
+ return string_contains_field_operation(self, other, field)
242
+ return string_contains_operation(self, other)
243
+
244
+ @overload
245
+ def split(self, separator: StringVar | str = "") -> ArrayVar[List[str]]: ...
246
+
247
+ @overload
248
+ def split(self, separator: NoReturn) -> NoReturn: ...
249
+
250
+ def split(self, separator: Any = "") -> ArrayVar[List[str]]:
251
+ """Split the string.
252
+
253
+ Args:
254
+ separator: The separator.
255
+
256
+ Returns:
257
+ The string split operation.
258
+ """
259
+ if not isinstance(separator, (StringVar, str)):
260
+ raise_unsupported_operand_types("split", (type(self), type(separator)))
261
+ return string_split_operation(self, separator)
262
+
263
+ @overload
264
+ def startswith(self, prefix: StringVar | str) -> BooleanVar: ...
265
+
266
+ @overload
267
+ def startswith(self, prefix: NoReturn) -> NoReturn: ...
268
+
269
+ def startswith(self, prefix: Any) -> BooleanVar:
270
+ """Check if the string starts with a prefix.
271
+
272
+ Args:
273
+ prefix: The prefix.
274
+
275
+ Returns:
276
+ The string starts with operation.
277
+ """
278
+ if not isinstance(prefix, (StringVar, str)):
279
+ raise_unsupported_operand_types("startswith", (type(self), type(prefix)))
280
+ return string_starts_with_operation(self, prefix)
281
+
282
+ @overload
283
+ def __lt__(self, other: StringVar | str) -> BooleanVar: ...
284
+
285
+ @overload
286
+ def __lt__(self, other: NoReturn) -> NoReturn: ...
287
+
288
+ def __lt__(self, other: Any):
289
+ """Check if the string is less than another string.
290
+
291
+ Args:
292
+ other: The other string.
293
+
294
+ Returns:
295
+ The string less than operation.
296
+ """
297
+ if not isinstance(other, (StringVar, str)):
298
+ raise_unsupported_operand_types("<", (type(self), type(other)))
299
+
300
+ return string_lt_operation(self, other)
301
+
302
+ @overload
303
+ def __gt__(self, other: StringVar | str) -> BooleanVar: ...
304
+
305
+ @overload
306
+ def __gt__(self, other: NoReturn) -> NoReturn: ...
307
+
308
+ def __gt__(self, other: Any):
309
+ """Check if the string is greater than another string.
310
+
311
+ Args:
312
+ other: The other string.
313
+
314
+ Returns:
315
+ The string greater than operation.
316
+ """
317
+ if not isinstance(other, (StringVar, str)):
318
+ raise_unsupported_operand_types(">", (type(self), type(other)))
319
+
320
+ return string_gt_operation(self, other)
321
+
322
+ @overload
323
+ def __le__(self, other: StringVar | str) -> BooleanVar: ...
324
+
325
+ @overload
326
+ def __le__(self, other: NoReturn) -> NoReturn: ...
327
+
328
+ def __le__(self, other: Any):
329
+ """Check if the string is less than or equal to another string.
330
+
331
+ Args:
332
+ other: The other string.
333
+
334
+ Returns:
335
+ The string less than or equal operation.
336
+ """
337
+ if not isinstance(other, (StringVar, str)):
338
+ raise_unsupported_operand_types("<=", (type(self), type(other)))
339
+
340
+ return string_le_operation(self, other)
341
+
342
+ @overload
343
+ def __ge__(self, other: StringVar | str) -> BooleanVar: ...
344
+
345
+ @overload
346
+ def __ge__(self, other: NoReturn) -> NoReturn: ...
347
+
348
+ def __ge__(self, other: Any):
349
+ """Check if the string is greater than or equal to another string.
350
+
351
+ Args:
352
+ other: The other string.
353
+
354
+ Returns:
355
+ The string greater than or equal operation.
356
+ """
357
+ if not isinstance(other, (StringVar, str)):
358
+ raise_unsupported_operand_types(">=", (type(self), type(other)))
359
+
360
+ return string_ge_operation(self, other)
361
+
362
+
363
+ @var_operation
364
+ def string_lt_operation(lhs: StringVar | str, rhs: StringVar | str):
365
+ """Check if a string is less than another string.
366
+
367
+ Args:
368
+ lhs: The left-hand side string.
369
+ rhs: The right-hand side string.
370
+
371
+ Returns:
372
+ The string less than operation.
373
+ """
374
+ return var_operation_return(js_expression=f"{lhs} < {rhs}", var_type=bool)
375
+
376
+
377
+ @var_operation
378
+ def string_gt_operation(lhs: StringVar | str, rhs: StringVar | str):
379
+ """Check if a string is greater than another string.
380
+
381
+ Args:
382
+ lhs: The left-hand side string.
383
+ rhs: The right-hand side string.
384
+
385
+ Returns:
386
+ The string greater than operation.
387
+ """
388
+ return var_operation_return(js_expression=f"{lhs} > {rhs}", var_type=bool)
389
+
390
+
391
+ @var_operation
392
+ def string_le_operation(lhs: StringVar | str, rhs: StringVar | str):
393
+ """Check if a string is less than or equal to another string.
394
+
395
+ Args:
396
+ lhs: The left-hand side string.
397
+ rhs: The right-hand side string.
398
+
399
+ Returns:
400
+ The string less than or equal operation.
401
+ """
402
+ return var_operation_return(js_expression=f"{lhs} <= {rhs}", var_type=bool)
403
+
404
+
405
+ @var_operation
406
+ def string_ge_operation(lhs: StringVar | str, rhs: StringVar | str):
407
+ """Check if a string is greater than or equal to another string.
408
+
409
+ Args:
410
+ lhs: The left-hand side string.
411
+ rhs: The right-hand side string.
412
+
413
+ Returns:
414
+ The string greater than or equal operation.
415
+ """
416
+ return var_operation_return(js_expression=f"{lhs} >= {rhs}", var_type=bool)
417
+
418
+
419
+ @var_operation
420
+ def string_lower_operation(string: StringVar):
421
+ """Convert a string to lowercase.
422
+
423
+ Args:
424
+ string: The string to convert.
425
+
426
+ Returns:
427
+ The lowercase string.
428
+ """
429
+ return var_operation_return(js_expression=f"{string}.toLowerCase()", var_type=str)
430
+
431
+
432
+ @var_operation
433
+ def string_upper_operation(string: StringVar):
434
+ """Convert a string to uppercase.
435
+
436
+ Args:
437
+ string: The string to convert.
438
+
439
+ Returns:
440
+ The uppercase string.
441
+ """
442
+ return var_operation_return(js_expression=f"{string}.toUpperCase()", var_type=str)
443
+
444
+
445
+ @var_operation
446
+ def string_strip_operation(string: StringVar):
447
+ """Strip a string.
448
+
449
+ Args:
450
+ string: The string to strip.
451
+
452
+ Returns:
453
+ The stripped string.
454
+ """
455
+ return var_operation_return(js_expression=f"{string}.trim()", var_type=str)
456
+
457
+
458
+ @var_operation
459
+ def string_contains_field_operation(
460
+ haystack: StringVar, needle: StringVar | str, field: StringVar | str
461
+ ):
462
+ """Check if a string contains another string.
463
+
464
+ Args:
465
+ haystack: The haystack.
466
+ needle: The needle.
467
+ field: The field to check.
468
+
469
+ Returns:
470
+ The string contains operation.
471
+ """
472
+ return var_operation_return(
473
+ js_expression=f"{haystack}.some(obj => obj[{field}] === {needle})",
474
+ var_type=bool,
475
+ )
476
+
477
+
478
+ @var_operation
479
+ def string_contains_operation(haystack: StringVar, needle: StringVar | str):
480
+ """Check if a string contains another string.
481
+
482
+ Args:
483
+ haystack: The haystack.
484
+ needle: The needle.
485
+
486
+ Returns:
487
+ The string contains operation.
488
+ """
489
+ return var_operation_return(
490
+ js_expression=f"{haystack}.includes({needle})", var_type=bool
491
+ )
492
+
493
+
494
+ @var_operation
495
+ def string_starts_with_operation(full_string: StringVar, prefix: StringVar | str):
496
+ """Check if a string starts with a prefix.
497
+
498
+ Args:
499
+ full_string: The full string.
500
+ prefix: The prefix.
501
+
502
+ Returns:
503
+ Whether the string starts with the prefix.
504
+ """
505
+ return var_operation_return(
506
+ js_expression=f"{full_string}.startsWith({prefix})", var_type=bool
507
+ )
508
+
509
+
510
+ @var_operation
511
+ def string_item_operation(string: StringVar, index: NumberVar | int):
512
+ """Get an item from a string.
513
+
514
+ Args:
515
+ string: The string.
516
+ index: The index of the item.
517
+
518
+ Returns:
519
+ The item from the string.
520
+ """
521
+ return var_operation_return(js_expression=f"{string}.at({index})", var_type=str)
522
+
523
+
524
+ @var_operation
525
+ def array_join_operation(array: ArrayVar, sep: StringVar | str = ""):
526
+ """Join the elements of an array.
527
+
528
+ Args:
529
+ array: The array.
530
+ sep: The separator.
531
+
532
+ Returns:
533
+ The joined elements.
534
+ """
535
+ return var_operation_return(js_expression=f"{array}.join({sep})", var_type=str)
536
+
537
+
538
+ # Compile regex for finding reflex var tags.
539
+ _decode_var_pattern_re = (
540
+ rf"{constants.REFLEX_VAR_OPENING_TAG}(.*?){constants.REFLEX_VAR_CLOSING_TAG}"
541
+ )
542
+ _decode_var_pattern = re.compile(_decode_var_pattern_re, flags=re.DOTALL)
543
+
544
+
545
+ @dataclasses.dataclass(
546
+ eq=False,
547
+ frozen=True,
548
+ **{"slots": True} if sys.version_info >= (3, 10) else {},
549
+ )
550
+ class LiteralStringVar(LiteralVar, StringVar):
551
+ """Base class for immutable literal string vars."""
552
+
553
+ _var_value: str = dataclasses.field(default="")
554
+
555
+ @classmethod
556
+ def create(
557
+ cls,
558
+ value: str,
559
+ _var_data: VarData | None = None,
560
+ ) -> StringVar:
561
+ """Create a var from a string value.
562
+
563
+ Args:
564
+ value: The value to create the var from.
565
+ _var_data: Additional hooks and imports associated with the Var.
566
+
567
+ Returns:
568
+ The var.
569
+ """
570
+ if REFLEX_VAR_OPENING_TAG in value:
571
+ strings_and_vals: list[ImmutableVar | str] = []
572
+ offset = 0
573
+
574
+ # Find all tags
575
+ while m := _decode_var_pattern.search(value):
576
+ start, end = m.span()
577
+
578
+ strings_and_vals.append(value[:start])
579
+
580
+ serialized_data = m.group(1)
581
+
582
+ if serialized_data.isnumeric() or (
583
+ serialized_data[0] == "-" and serialized_data[1:].isnumeric()
584
+ ):
585
+ # This is a global immutable var.
586
+ var = _global_vars[int(serialized_data)]
587
+ strings_and_vals.append(var)
588
+ value = value[(end + len(var._var_name)) :]
589
+
590
+ offset += end - start
591
+
592
+ strings_and_vals.append(value)
593
+
594
+ filtered_strings_and_vals = [
595
+ s for s in strings_and_vals if isinstance(s, ImmutableVar) or s
596
+ ]
597
+
598
+ if len(filtered_strings_and_vals) == 1:
599
+ return LiteralVar.create(filtered_strings_and_vals[0]).to(StringVar)
600
+
601
+ return ConcatVarOperation.create(
602
+ *filtered_strings_and_vals,
603
+ _var_data=_var_data,
604
+ )
605
+
606
+ return LiteralStringVar(
607
+ _var_name=json.dumps(value),
608
+ _var_type=str,
609
+ _var_data=_var_data,
610
+ _var_value=value,
611
+ )
612
+
613
+ def __hash__(self) -> int:
614
+ """Get the hash of the var.
615
+
616
+ Returns:
617
+ The hash of the var.
618
+ """
619
+ return hash((self.__class__.__name__, self._var_value))
620
+
621
+ def json(self) -> str:
622
+ """Get the JSON representation of the var.
623
+
624
+ Returns:
625
+ The JSON representation of the var.
626
+ """
627
+ return json.dumps(self._var_value)
628
+
629
+
630
+ @dataclasses.dataclass(
631
+ eq=False,
632
+ frozen=True,
633
+ **{"slots": True} if sys.version_info >= (3, 10) else {},
634
+ )
635
+ class ConcatVarOperation(CachedVarOperation, StringVar):
636
+ """Representing a concatenation of literal string vars."""
637
+
638
+ _var_value: Tuple[ImmutableVar, ...] = dataclasses.field(default_factory=tuple)
639
+
640
+ @cached_property_no_lock
641
+ def _cached_var_name(self) -> str:
642
+ """The name of the var.
643
+
644
+ Returns:
645
+ The name of the var.
646
+ """
647
+ list_of_strs: List[Union[str, ImmutableVar]] = []
648
+ last_string = ""
649
+ for var in self._var_value:
650
+ if isinstance(var, LiteralStringVar):
651
+ last_string += var._var_value
652
+ else:
653
+ if last_string:
654
+ list_of_strs.append(last_string)
655
+ last_string = ""
656
+ list_of_strs.append(var)
657
+
658
+ if last_string:
659
+ list_of_strs.append(last_string)
660
+
661
+ list_of_strs_filtered = [
662
+ str(LiteralVar.create(s))
663
+ for s in list_of_strs
664
+ if isinstance(s, ImmutableVar) or s
665
+ ]
666
+
667
+ if len(list_of_strs_filtered) == 1:
668
+ return list_of_strs_filtered[0]
669
+
670
+ return "(" + "+".join(list_of_strs_filtered) + ")"
671
+
672
+ @cached_property_no_lock
673
+ def _cached_get_all_var_data(self) -> VarData | None:
674
+ """Get all the VarData asVarDatae Var.
675
+
676
+ Returns:
677
+ The VarData associated with the Var.
678
+ """
679
+ return VarData.merge(
680
+ *[
681
+ var._get_all_var_data()
682
+ for var in self._var_value
683
+ if isinstance(var, ImmutableVar)
684
+ ],
685
+ self._var_data,
686
+ )
687
+
688
+ @classmethod
689
+ def create(
690
+ cls,
691
+ *value: Var | str,
692
+ _var_data: VarData | None = None,
693
+ ) -> ConcatVarOperation:
694
+ """Create a var from a string value.
695
+
696
+ Args:
697
+ value: The values to concatenate.
698
+ _var_data: Additional hooks and imports associated with the Var.
699
+
700
+ Returns:
701
+ The var.
702
+ """
703
+ return cls(
704
+ _var_name="",
705
+ _var_type=str,
706
+ _var_data=_var_data,
707
+ _var_value=tuple(map(LiteralVar.create, value)),
708
+ )
709
+
710
+
711
+ ARRAY_VAR_TYPE = TypeVar("ARRAY_VAR_TYPE", bound=Union[List, Tuple, Set])
712
+
713
+ OTHER_TUPLE = TypeVar("OTHER_TUPLE")
714
+
715
+ INNER_ARRAY_VAR = TypeVar("INNER_ARRAY_VAR")
716
+
717
+ KEY_TYPE = TypeVar("KEY_TYPE")
718
+ VALUE_TYPE = TypeVar("VALUE_TYPE")
719
+
720
+
721
+ class ArrayVar(ImmutableVar[ARRAY_VAR_TYPE]):
722
+ """Base class for immutable array vars."""
723
+
724
+ @overload
725
+ def join(self, sep: StringVar | str = "") -> StringVar: ...
726
+
727
+ @overload
728
+ def join(self, sep: NoReturn) -> NoReturn: ...
729
+
730
+ def join(self, sep: Any = "") -> StringVar:
731
+ """Join the elements of the array.
732
+
733
+ Args:
734
+ sep: The separator between elements.
735
+
736
+ Returns:
737
+ The joined elements.
738
+ """
739
+ if not isinstance(sep, (StringVar, str)):
740
+ raise_unsupported_operand_types("join", (type(self), type(sep)))
741
+ return array_join_operation(self, sep)
742
+
743
+ def reverse(self) -> ArrayVar[ARRAY_VAR_TYPE]:
744
+ """Reverse the array.
745
+
746
+ Returns:
747
+ The reversed array.
748
+ """
749
+ return array_reverse_operation(self)
750
+
751
+ @overload
752
+ def __add__(self, other: ArrayVar[ARRAY_VAR_TYPE]) -> ArrayVar[ARRAY_VAR_TYPE]: ...
753
+
754
+ @overload
755
+ def __add__(self, other: NoReturn) -> NoReturn: ...
756
+
757
+ def __add__(self, other: Any) -> ArrayVar[ARRAY_VAR_TYPE]:
758
+ """Concatenate two arrays.
759
+
760
+ Parameters:
761
+ other: The other array to concatenate.
762
+
763
+ Returns:
764
+ ArrayConcatOperation: The concatenation of the two arrays.
765
+ """
766
+ if not isinstance(other, ArrayVar):
767
+ raise_unsupported_operand_types("+", (type(self), type(other)))
768
+
769
+ return array_concat_operation(self, other)
770
+
771
+ @overload
772
+ def __getitem__(self, i: slice) -> ArrayVar[ARRAY_VAR_TYPE]: ...
773
+
774
+ @overload
775
+ def __getitem__(
776
+ self: (
777
+ ArrayVar[Tuple[int, OTHER_TUPLE]]
778
+ | ArrayVar[Tuple[float, OTHER_TUPLE]]
779
+ | ArrayVar[Tuple[int | float, OTHER_TUPLE]]
780
+ ),
781
+ i: Literal[0, -2],
782
+ ) -> NumberVar: ...
783
+
784
+ @overload
785
+ def __getitem__(
786
+ self: (
787
+ ArrayVar[Tuple[OTHER_TUPLE, int]]
788
+ | ArrayVar[Tuple[OTHER_TUPLE, float]]
789
+ | ArrayVar[Tuple[OTHER_TUPLE, int | float]]
790
+ ),
791
+ i: Literal[1, -1],
792
+ ) -> NumberVar: ...
793
+
794
+ @overload
795
+ def __getitem__(
796
+ self: ArrayVar[Tuple[str, OTHER_TUPLE]], i: Literal[0, -2]
797
+ ) -> StringVar: ...
798
+
799
+ @overload
800
+ def __getitem__(
801
+ self: ArrayVar[Tuple[OTHER_TUPLE, str]], i: Literal[1, -1]
802
+ ) -> StringVar: ...
803
+
804
+ @overload
805
+ def __getitem__(
806
+ self: ArrayVar[Tuple[bool, OTHER_TUPLE]], i: Literal[0, -2]
807
+ ) -> BooleanVar: ...
808
+
809
+ @overload
810
+ def __getitem__(
811
+ self: ArrayVar[Tuple[OTHER_TUPLE, bool]], i: Literal[1, -1]
812
+ ) -> BooleanVar: ...
813
+
814
+ @overload
815
+ def __getitem__(
816
+ self: (
817
+ ARRAY_VAR_OF_LIST_ELEMENT[int]
818
+ | ARRAY_VAR_OF_LIST_ELEMENT[float]
819
+ | ARRAY_VAR_OF_LIST_ELEMENT[int | float]
820
+ ),
821
+ i: int | NumberVar,
822
+ ) -> NumberVar: ...
823
+
824
+ @overload
825
+ def __getitem__(
826
+ self: ARRAY_VAR_OF_LIST_ELEMENT[str], i: int | NumberVar
827
+ ) -> StringVar: ...
828
+
829
+ @overload
830
+ def __getitem__(
831
+ self: ARRAY_VAR_OF_LIST_ELEMENT[bool], i: int | NumberVar
832
+ ) -> BooleanVar: ...
833
+
834
+ @overload
835
+ def __getitem__(
836
+ self: ARRAY_VAR_OF_LIST_ELEMENT[List[INNER_ARRAY_VAR]],
837
+ i: int | NumberVar,
838
+ ) -> ArrayVar[List[INNER_ARRAY_VAR]]: ...
839
+
840
+ @overload
841
+ def __getitem__(
842
+ self: ARRAY_VAR_OF_LIST_ELEMENT[Set[INNER_ARRAY_VAR]],
843
+ i: int | NumberVar,
844
+ ) -> ArrayVar[Set[INNER_ARRAY_VAR]]: ...
845
+
846
+ @overload
847
+ def __getitem__(
848
+ self: ARRAY_VAR_OF_LIST_ELEMENT[Tuple[INNER_ARRAY_VAR, ...]],
849
+ i: int | NumberVar,
850
+ ) -> ArrayVar[Tuple[INNER_ARRAY_VAR, ...]]: ...
851
+
852
+ @overload
853
+ def __getitem__(
854
+ self: ARRAY_VAR_OF_LIST_ELEMENT[Dict[KEY_TYPE, VALUE_TYPE]],
855
+ i: int | NumberVar,
856
+ ) -> ObjectVar[Dict[KEY_TYPE, VALUE_TYPE]]: ...
857
+
858
+ @overload
859
+ def __getitem__(self, i: int | NumberVar) -> ImmutableVar: ...
860
+
861
+ def __getitem__(self, i: Any) -> ArrayVar[ARRAY_VAR_TYPE] | ImmutableVar:
862
+ """Get a slice of the array.
863
+
864
+ Args:
865
+ i: The slice.
866
+
867
+ Returns:
868
+ The array slice operation.
869
+ """
870
+ if isinstance(i, slice):
871
+ return ArraySliceOperation.create(self, i)
872
+ if not isinstance(i, (int, NumberVar)) or (
873
+ isinstance(i, NumberVar) and i._is_strict_float()
874
+ ):
875
+ raise_unsupported_operand_types("[]", (type(self), type(i)))
876
+ return array_item_operation(self, i)
877
+
878
+ def length(self) -> NumberVar:
879
+ """Get the length of the array.
880
+
881
+ Returns:
882
+ The length of the array.
883
+ """
884
+ return array_length_operation(self)
885
+
886
+ @overload
887
+ @classmethod
888
+ def range(cls, stop: int | NumberVar, /) -> ArrayVar[List[int]]: ...
889
+
890
+ @overload
891
+ @classmethod
892
+ def range(
893
+ cls,
894
+ start: int | NumberVar,
895
+ end: int | NumberVar,
896
+ step: int | NumberVar = 1,
897
+ /,
898
+ ) -> ArrayVar[List[int]]: ...
899
+
900
+ @classmethod
901
+ def range(
902
+ cls,
903
+ first_endpoint: int | NumberVar,
904
+ second_endpoint: int | NumberVar | None = None,
905
+ step: int | NumberVar | None = None,
906
+ ) -> ArrayVar[List[int]]:
907
+ """Create a range of numbers.
908
+
909
+ Args:
910
+ first_endpoint: The end of the range if second_endpoint is not provided, otherwise the start of the range.
911
+ second_endpoint: The end of the range.
912
+ step: The step of the range.
913
+
914
+ Returns:
915
+ The range of numbers.
916
+ """
917
+ if any(
918
+ not isinstance(i, (int, NumberVar))
919
+ for i in (first_endpoint, second_endpoint, step)
920
+ if i is not None
921
+ ):
922
+ raise_unsupported_operand_types(
923
+ "range", (type(first_endpoint), type(second_endpoint), type(step))
924
+ )
925
+ if second_endpoint is None:
926
+ start = 0
927
+ end = first_endpoint
928
+ else:
929
+ start = first_endpoint
930
+ end = second_endpoint
931
+
932
+ return array_range_operation(start, end, step or 1)
933
+
934
+ @overload
935
+ def contains(self, other: Any) -> BooleanVar: ...
936
+
937
+ @overload
938
+ def contains(self, other: Any, field: StringVar | str) -> BooleanVar: ...
939
+
940
+ def contains(self, other: Any, field: Any = None) -> BooleanVar:
941
+ """Check if the array contains an element.
942
+
943
+ Args:
944
+ other: The element to check for.
945
+ field: The field to check.
946
+
947
+ Returns:
948
+ The array contains operation.
949
+ """
950
+ if field is not None:
951
+ if not isinstance(field, (StringVar, str)):
952
+ raise_unsupported_operand_types("contains", (type(self), type(field)))
953
+ return array_contains_field_operation(self, other, field)
954
+ return array_contains_operation(self, other)
955
+
956
+ def pluck(self, field: StringVar | str) -> ArrayVar:
957
+ """Pluck a field from the array.
958
+
959
+ Args:
960
+ field: The field to pluck from the array.
961
+
962
+ Returns:
963
+ The array pluck operation.
964
+ """
965
+ return array_pluck_operation(self, field)
966
+
967
+ @overload
968
+ def __mul__(self, other: NumberVar | int) -> ArrayVar[ARRAY_VAR_TYPE]: ...
969
+
970
+ @overload
971
+ def __mul__(self, other: NoReturn) -> NoReturn: ...
972
+
973
+ def __mul__(self, other: Any) -> ArrayVar[ARRAY_VAR_TYPE]:
974
+ """Multiply the sequence by a number or integer.
975
+
976
+ Parameters:
977
+ other: The number or integer to multiply the sequence by.
978
+
979
+ Returns:
980
+ ArrayVar[ARRAY_VAR_TYPE]: The result of multiplying the sequence by the given number or integer.
981
+ """
982
+ if not isinstance(other, (NumberVar, int)) or (
983
+ isinstance(other, NumberVar) and other._is_strict_float()
984
+ ):
985
+ raise_unsupported_operand_types("*", (type(self), type(other)))
986
+
987
+ return repeat_array_operation(self, other)
988
+
989
+ __rmul__ = __mul__ # type: ignore
990
+
991
+ @overload
992
+ def __lt__(self, other: ArrayVar[ARRAY_VAR_TYPE]) -> BooleanVar: ...
993
+
994
+ @overload
995
+ def __lt__(self, other: list | tuple) -> BooleanVar: ...
996
+
997
+ def __lt__(self, other: Any):
998
+ """Check if the array is less than another array.
999
+
1000
+ Args:
1001
+ other: The other array.
1002
+
1003
+ Returns:
1004
+ The array less than operation.
1005
+ """
1006
+ if not isinstance(other, (ArrayVar, list, tuple)):
1007
+ raise_unsupported_operand_types("<", (type(self), type(other)))
1008
+
1009
+ return array_lt_operation(self, other)
1010
+
1011
+ @overload
1012
+ def __gt__(self, other: ArrayVar[ARRAY_VAR_TYPE]) -> BooleanVar: ...
1013
+
1014
+ @overload
1015
+ def __gt__(self, other: list | tuple) -> BooleanVar: ...
1016
+
1017
+ def __gt__(self, other: Any):
1018
+ """Check if the array is greater than another array.
1019
+
1020
+ Args:
1021
+ other: The other array.
1022
+
1023
+ Returns:
1024
+ The array greater than operation.
1025
+ """
1026
+ if not isinstance(other, (ArrayVar, list, tuple)):
1027
+ raise_unsupported_operand_types(">", (type(self), type(other)))
1028
+
1029
+ return array_gt_operation(self, other)
1030
+
1031
+ @overload
1032
+ def __le__(self, other: ArrayVar[ARRAY_VAR_TYPE]) -> BooleanVar: ...
1033
+
1034
+ @overload
1035
+ def __le__(self, other: list | tuple) -> BooleanVar: ...
1036
+
1037
+ def __le__(self, other: Any):
1038
+ """Check if the array is less than or equal to another array.
1039
+
1040
+ Args:
1041
+ other: The other array.
1042
+
1043
+ Returns:
1044
+ The array less than or equal operation.
1045
+ """
1046
+ if not isinstance(other, (ArrayVar, list, tuple)):
1047
+ raise_unsupported_operand_types("<=", (type(self), type(other)))
1048
+
1049
+ return array_le_operation(self, other)
1050
+
1051
+ @overload
1052
+ def __ge__(self, other: ArrayVar[ARRAY_VAR_TYPE]) -> BooleanVar: ...
1053
+
1054
+ @overload
1055
+ def __ge__(self, other: list | tuple) -> BooleanVar: ...
1056
+
1057
+ def __ge__(self, other: Any):
1058
+ """Check if the array is greater than or equal to another array.
1059
+
1060
+ Args:
1061
+ other: The other array.
1062
+
1063
+ Returns:
1064
+ The array greater than or equal operation.
1065
+ """
1066
+ if not isinstance(other, (ArrayVar, list, tuple)):
1067
+ raise_unsupported_operand_types(">=", (type(self), type(other)))
1068
+
1069
+ return array_ge_operation(self, other)
1070
+
1071
+ def foreach(self, fn: Any):
1072
+ """Apply a function to each element of the array.
1073
+
1074
+ Args:
1075
+ fn: The function to apply.
1076
+
1077
+ Returns:
1078
+ The array after applying the function.
1079
+
1080
+ Raises:
1081
+ VarTypeError: If the function takes more than one argument.
1082
+ """
1083
+ from .function import ArgsFunctionOperation
1084
+
1085
+ if not callable(fn):
1086
+ raise_unsupported_operand_types("foreach", (type(self), type(fn)))
1087
+ # get the number of arguments of the function
1088
+ num_args = len(inspect.signature(fn).parameters)
1089
+ if num_args > 1:
1090
+ raise VarTypeError(
1091
+ "The function passed to foreach should take at most one argument."
1092
+ )
1093
+
1094
+ if num_args == 0:
1095
+ return_value = fn()
1096
+ function_var = ArgsFunctionOperation.create(tuple(), return_value)
1097
+ else:
1098
+ # generic number var
1099
+ number_var = ImmutableVar("").to(NumberVar)
1100
+
1101
+ first_arg_type = self[number_var]._var_type
1102
+
1103
+ arg_name = get_unique_variable_name()
1104
+
1105
+ # get first argument type
1106
+ first_arg = ImmutableVar(
1107
+ _var_name=arg_name,
1108
+ _var_type=first_arg_type,
1109
+ ).guess_type()
1110
+
1111
+ function_var = ArgsFunctionOperation.create((arg_name,), fn(first_arg))
1112
+
1113
+ return map_array_operation(self, function_var)
1114
+
1115
+
1116
+ LIST_ELEMENT = TypeVar("LIST_ELEMENT")
1117
+
1118
+ ARRAY_VAR_OF_LIST_ELEMENT = Union[
1119
+ ArrayVar[List[LIST_ELEMENT]],
1120
+ ArrayVar[Set[LIST_ELEMENT]],
1121
+ ArrayVar[Tuple[LIST_ELEMENT, ...]],
1122
+ ]
1123
+
1124
+
1125
+ @dataclasses.dataclass(
1126
+ eq=False,
1127
+ frozen=True,
1128
+ **{"slots": True} if sys.version_info >= (3, 10) else {},
1129
+ )
1130
+ class LiteralArrayVar(CachedVarOperation, LiteralVar, ArrayVar[ARRAY_VAR_TYPE]):
1131
+ """Base class for immutable literal array vars."""
1132
+
1133
+ _var_value: Union[
1134
+ List[Union[ImmutableVar, Any]],
1135
+ Set[Union[ImmutableVar, Any]],
1136
+ Tuple[Union[ImmutableVar, Any], ...],
1137
+ ] = dataclasses.field(default_factory=list)
1138
+
1139
+ @cached_property_no_lock
1140
+ def _cached_var_name(self) -> str:
1141
+ """The name of the var.
1142
+
1143
+ Returns:
1144
+ The name of the var.
1145
+ """
1146
+ return (
1147
+ "["
1148
+ + ", ".join(
1149
+ [str(LiteralVar.create(element)) for element in self._var_value]
1150
+ )
1151
+ + "]"
1152
+ )
1153
+
1154
+ @cached_property_no_lock
1155
+ def _cached_get_all_var_data(self) -> VarData | None:
1156
+ """Get all the VarData associated with the Var.
1157
+
1158
+ Returns:
1159
+ The VarData associated with the Var.
1160
+ """
1161
+ return VarData.merge(
1162
+ *[
1163
+ LiteralVar.create(element)._get_all_var_data()
1164
+ for element in self._var_value
1165
+ ],
1166
+ self._var_data,
1167
+ )
1168
+
1169
+ def __hash__(self) -> int:
1170
+ """Get the hash of the var.
1171
+
1172
+ Returns:
1173
+ The hash of the var.
1174
+ """
1175
+ return hash((self.__class__.__name__, self._var_name))
1176
+
1177
+ def json(self) -> str:
1178
+ """Get the JSON representation of the var.
1179
+
1180
+ Returns:
1181
+ The JSON representation of the var.
1182
+ """
1183
+ return (
1184
+ "["
1185
+ + ", ".join(
1186
+ [LiteralVar.create(element).json() for element in self._var_value]
1187
+ )
1188
+ + "]"
1189
+ )
1190
+
1191
+ @classmethod
1192
+ def create(
1193
+ cls,
1194
+ value: ARRAY_VAR_TYPE,
1195
+ _var_type: Type[ARRAY_VAR_TYPE] | None = None,
1196
+ _var_data: VarData | None = None,
1197
+ ) -> LiteralArrayVar[ARRAY_VAR_TYPE]:
1198
+ """Create a var from a string value.
1199
+
1200
+ Args:
1201
+ value: The value to create the var from.
1202
+ _var_data: Additional hooks and imports associated with the Var.
1203
+
1204
+ Returns:
1205
+ The var.
1206
+ """
1207
+ return cls(
1208
+ _var_name="",
1209
+ _var_type=figure_out_type(value) if _var_type is None else _var_type,
1210
+ _var_data=_var_data,
1211
+ _var_value=value,
1212
+ )
1213
+
1214
+
1215
+ @var_operation
1216
+ def string_split_operation(string: StringVar, sep: StringVar | str = ""):
1217
+ """Split a string.
1218
+
1219
+ Args:
1220
+ string: The string to split.
1221
+ sep: The separator.
1222
+
1223
+ Returns:
1224
+ The split string.
1225
+ """
1226
+ return var_operation_return(
1227
+ js_expression=f"{string}.split({sep})", var_type=List[str]
1228
+ )
1229
+
1230
+
1231
+ @dataclasses.dataclass(
1232
+ eq=False,
1233
+ frozen=True,
1234
+ **{"slots": True} if sys.version_info >= (3, 10) else {},
1235
+ )
1236
+ class ArraySliceOperation(CachedVarOperation, ArrayVar):
1237
+ """Base class for immutable string vars that are the result of a string slice operation."""
1238
+
1239
+ _array: ArrayVar = dataclasses.field(
1240
+ default_factory=lambda: LiteralArrayVar.create([])
1241
+ )
1242
+ _start: NumberVar | int = dataclasses.field(default_factory=lambda: 0)
1243
+ _stop: NumberVar | int = dataclasses.field(default_factory=lambda: 0)
1244
+ _step: NumberVar | int = dataclasses.field(default_factory=lambda: 1)
1245
+
1246
+ @cached_property_no_lock
1247
+ def _cached_var_name(self) -> str:
1248
+ """The name of the var.
1249
+
1250
+ Returns:
1251
+ The name of the var.
1252
+
1253
+ Raises:
1254
+ ValueError: If the slice step is zero.
1255
+ """
1256
+ start, end, step = self._start, self._stop, self._step
1257
+
1258
+ normalized_start = (
1259
+ LiteralVar.create(start)
1260
+ if start is not None
1261
+ else ImmutableVar.create_safe("undefined")
1262
+ )
1263
+ normalized_end = (
1264
+ LiteralVar.create(end)
1265
+ if end is not None
1266
+ else ImmutableVar.create_safe("undefined")
1267
+ )
1268
+ if step is None:
1269
+ return f"{str(self._array)}.slice({str(normalized_start)}, {str(normalized_end)})"
1270
+ if not isinstance(step, ImmutableVar):
1271
+ if step < 0:
1272
+ actual_start = end + 1 if end is not None else 0
1273
+ actual_end = start + 1 if start is not None else self._array.length()
1274
+ return str(self._array[actual_start:actual_end].reverse()[::-step])
1275
+ if step == 0:
1276
+ raise ValueError("slice step cannot be zero")
1277
+ return f"{str(self._array)}.slice({str(normalized_start)}, {str(normalized_end)}).filter((_, i) => i % {str(step)} === 0)"
1278
+
1279
+ actual_start_reverse = end + 1 if end is not None else 0
1280
+ actual_end_reverse = start + 1 if start is not None else self._array.length()
1281
+
1282
+ return f"{str(self.step)} > 0 ? {str(self._array)}.slice({str(normalized_start)}, {str(normalized_end)}).filter((_, i) => i % {str(step)} === 0) : {str(self._array)}.slice({str(actual_start_reverse)}, {str(actual_end_reverse)}).reverse().filter((_, i) => i % {str(-step)} === 0)"
1283
+
1284
+ @classmethod
1285
+ def create(
1286
+ cls,
1287
+ array: ArrayVar,
1288
+ slice: slice,
1289
+ _var_data: VarData | None = None,
1290
+ ) -> ArraySliceOperation:
1291
+ """Create a var from a string value.
1292
+
1293
+ Args:
1294
+ array: The array.
1295
+ slice: The slice.
1296
+ _var_data: Additional hooks and imports associated with the Var.
1297
+
1298
+ Returns:
1299
+ The var.
1300
+ """
1301
+ return cls(
1302
+ _var_name="",
1303
+ _var_type=array._var_type,
1304
+ _var_data=_var_data,
1305
+ _array=array,
1306
+ _start=slice.start,
1307
+ _stop=slice.stop,
1308
+ _step=slice.step,
1309
+ )
1310
+
1311
+
1312
+ @var_operation
1313
+ def array_pluck_operation(
1314
+ array: ArrayVar[ARRAY_VAR_TYPE],
1315
+ field: StringVar | str,
1316
+ ) -> CustomVarOperationReturn[ARRAY_VAR_TYPE]:
1317
+ """Pluck a field from an array of objects.
1318
+
1319
+ Args:
1320
+ array: The array to pluck from.
1321
+ field: The field to pluck from the objects in the array.
1322
+
1323
+ Returns:
1324
+ The reversed array.
1325
+ """
1326
+ return var_operation_return(
1327
+ js_expression=f"{array}.map(e=>e?.[{field}])",
1328
+ var_type=array._var_type,
1329
+ )
1330
+
1331
+
1332
+ @var_operation
1333
+ def array_reverse_operation(
1334
+ array: ArrayVar[ARRAY_VAR_TYPE],
1335
+ ) -> CustomVarOperationReturn[ARRAY_VAR_TYPE]:
1336
+ """Reverse an array.
1337
+
1338
+ Args:
1339
+ array: The array to reverse.
1340
+
1341
+ Returns:
1342
+ The reversed array.
1343
+ """
1344
+ return var_operation_return(
1345
+ js_expression=f"{array}.slice().reverse()",
1346
+ var_type=array._var_type,
1347
+ )
1348
+
1349
+
1350
+ @var_operation
1351
+ def array_lt_operation(lhs: ArrayVar | list | tuple, rhs: ArrayVar | list | tuple):
1352
+ """Check if an array is less than another array.
1353
+
1354
+ Args:
1355
+ lhs: The left-hand side array.
1356
+ rhs: The right-hand side array.
1357
+
1358
+ Returns:
1359
+ The array less than operation.
1360
+ """
1361
+ return var_operation_return(js_expression=f"{lhs} < {rhs}", var_type=bool)
1362
+
1363
+
1364
+ @var_operation
1365
+ def array_gt_operation(lhs: ArrayVar | list | tuple, rhs: ArrayVar | list | tuple):
1366
+ """Check if an array is greater than another array.
1367
+
1368
+ Args:
1369
+ lhs: The left-hand side array.
1370
+ rhs: The right-hand side array.
1371
+
1372
+ Returns:
1373
+ The array greater than operation.
1374
+ """
1375
+ return var_operation_return(js_expression=f"{lhs} > {rhs}", var_type=bool)
1376
+
1377
+
1378
+ @var_operation
1379
+ def array_le_operation(lhs: ArrayVar | list | tuple, rhs: ArrayVar | list | tuple):
1380
+ """Check if an array is less than or equal to another array.
1381
+
1382
+ Args:
1383
+ lhs: The left-hand side array.
1384
+ rhs: The right-hand side array.
1385
+
1386
+ Returns:
1387
+ The array less than or equal operation.
1388
+ """
1389
+ return var_operation_return(js_expression=f"{lhs} <= {rhs}", var_type=bool)
1390
+
1391
+
1392
+ @var_operation
1393
+ def array_ge_operation(lhs: ArrayVar | list | tuple, rhs: ArrayVar | list | tuple):
1394
+ """Check if an array is greater than or equal to another array.
1395
+
1396
+ Args:
1397
+ lhs: The left-hand side array.
1398
+ rhs: The right-hand side array.
1399
+
1400
+ Returns:
1401
+ The array greater than or equal operation.
1402
+ """
1403
+ return var_operation_return(js_expression=f"{lhs} >= {rhs}", var_type=bool)
1404
+
1405
+
1406
+ @var_operation
1407
+ def array_length_operation(array: ArrayVar):
1408
+ """Get the length of an array.
1409
+
1410
+ Args:
1411
+ array: The array.
1412
+
1413
+ Returns:
1414
+ The length of the array.
1415
+ """
1416
+ return var_operation_return(
1417
+ js_expression=f"{array}.length",
1418
+ var_type=int,
1419
+ )
1420
+
1421
+
1422
+ def is_tuple_type(t: GenericType) -> bool:
1423
+ """Check if a type is a tuple type.
1424
+
1425
+ Args:
1426
+ t: The type to check.
1427
+
1428
+ Returns:
1429
+ Whether the type is a tuple type.
1430
+ """
1431
+ if inspect.isclass(t):
1432
+ return issubclass(t, tuple)
1433
+ return get_origin(t) is tuple
1434
+
1435
+
1436
+ @var_operation
1437
+ def array_item_operation(array: ArrayVar, index: NumberVar | int):
1438
+ """Get an item from an array.
1439
+
1440
+ Args:
1441
+ array: The array.
1442
+ index: The index of the item.
1443
+
1444
+ Returns:
1445
+ The item from the array.
1446
+ """
1447
+ args = typing.get_args(array._var_type)
1448
+ if args and isinstance(index, LiteralNumberVar) and is_tuple_type(array._var_type):
1449
+ index_value = int(index._var_value)
1450
+ element_type = args[index_value % len(args)]
1451
+ else:
1452
+ element_type = unionize(*args)
1453
+
1454
+ return var_operation_return(
1455
+ js_expression=f"{str(array)}.at({str(index)})",
1456
+ var_type=element_type,
1457
+ )
1458
+
1459
+
1460
+ @var_operation
1461
+ def array_range_operation(
1462
+ start: NumberVar | int, stop: NumberVar | int, step: NumberVar | int
1463
+ ):
1464
+ """Create a range of numbers.
1465
+
1466
+ Args:
1467
+ start: The start of the range.
1468
+ stop: The end of the range.
1469
+ step: The step of the range.
1470
+
1471
+ Returns:
1472
+ The range of numbers.
1473
+ """
1474
+ return var_operation_return(
1475
+ js_expression=f"Array.from({{ length: ({str(stop)} - {str(start)}) / {str(step)} }}, (_, i) => {str(start)} + i * {str(step)})",
1476
+ var_type=List[int],
1477
+ )
1478
+
1479
+
1480
+ @var_operation
1481
+ def array_contains_field_operation(
1482
+ haystack: ArrayVar, needle: Any | Var, field: StringVar | str
1483
+ ):
1484
+ """Check if an array contains an element.
1485
+
1486
+ Args:
1487
+ haystack: The array to check.
1488
+ needle: The element to check for.
1489
+ field: The field to check.
1490
+
1491
+ Returns:
1492
+ The array contains operation.
1493
+ """
1494
+ return var_operation_return(
1495
+ js_expression=f"{haystack}.some(obj => obj[{field}] === {needle})",
1496
+ var_type=bool,
1497
+ )
1498
+
1499
+
1500
+ @var_operation
1501
+ def array_contains_operation(haystack: ArrayVar, needle: Any | Var):
1502
+ """Check if an array contains an element.
1503
+
1504
+ Args:
1505
+ haystack: The array to check.
1506
+ needle: The element to check for.
1507
+
1508
+ Returns:
1509
+ The array contains operation.
1510
+ """
1511
+ return var_operation_return(
1512
+ js_expression=f"{haystack}.includes({needle})",
1513
+ var_type=bool,
1514
+ )
1515
+
1516
+
1517
+ @dataclasses.dataclass(
1518
+ eq=False,
1519
+ frozen=True,
1520
+ **{"slots": True} if sys.version_info >= (3, 10) else {},
1521
+ )
1522
+ class ToStringOperation(ToOperation, StringVar):
1523
+ """Base class for immutable string vars that are the result of a to string operation."""
1524
+
1525
+ _original: Var = dataclasses.field(default_factory=lambda: LiteralNoneVar.create())
1526
+
1527
+ _default_var_type: ClassVar[Type] = str
1528
+
1529
+
1530
+ @dataclasses.dataclass(
1531
+ eq=False,
1532
+ frozen=True,
1533
+ **{"slots": True} if sys.version_info >= (3, 10) else {},
1534
+ )
1535
+ class ToArrayOperation(ToOperation, ArrayVar):
1536
+ """Base class for immutable array vars that are the result of a to array operation."""
1537
+
1538
+ _original: Var = dataclasses.field(default_factory=lambda: LiteralNoneVar.create())
1539
+
1540
+ _default_var_type: ClassVar[Type] = List[Any]
1541
+
1542
+
1543
+ @var_operation
1544
+ def repeat_array_operation(
1545
+ array: ArrayVar[ARRAY_VAR_TYPE], count: NumberVar | int
1546
+ ) -> CustomVarOperationReturn[ARRAY_VAR_TYPE]:
1547
+ """Repeat an array a number of times.
1548
+
1549
+ Args:
1550
+ array: The array to repeat.
1551
+ count: The number of times to repeat the array.
1552
+
1553
+ Returns:
1554
+ The repeated array.
1555
+ """
1556
+ return var_operation_return(
1557
+ js_expression=f"Array.from({{ length: {count} }}).flatMap(() => {array})",
1558
+ var_type=array._var_type,
1559
+ )
1560
+
1561
+
1562
+ if TYPE_CHECKING:
1563
+ from .function import FunctionVar
1564
+
1565
+
1566
+ @var_operation
1567
+ def map_array_operation(
1568
+ array: ArrayVar[ARRAY_VAR_TYPE],
1569
+ function: FunctionVar,
1570
+ ):
1571
+ """Map a function over an array.
1572
+
1573
+ Args:
1574
+ array: The array.
1575
+ function: The function to map.
1576
+
1577
+ Returns:
1578
+ The mapped array.
1579
+ """
1580
+ return var_operation_return(
1581
+ js_expression=f"{array}.map({function})", var_type=List[Any]
1582
+ )
1583
+
1584
+
1585
+ @var_operation
1586
+ def array_concat_operation(
1587
+ lhs: ArrayVar[ARRAY_VAR_TYPE], rhs: ArrayVar[ARRAY_VAR_TYPE]
1588
+ ) -> CustomVarOperationReturn[ARRAY_VAR_TYPE]:
1589
+ """Concatenate two arrays.
1590
+
1591
+ Args:
1592
+ lhs: The left-hand side array.
1593
+ rhs: The right-hand side array.
1594
+
1595
+ Returns:
1596
+ The concatenated array.
1597
+ """
1598
+ return var_operation_return(
1599
+ js_expression=f"[...{lhs}, ...{rhs}]",
1600
+ var_type=Union[lhs._var_type, rhs._var_type],
1601
+ )