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