sequence.py 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612
  1. """Collection of string classes and utilities."""
  2. from __future__ import annotations
  3. import dataclasses
  4. import inspect
  5. import json
  6. import re
  7. import sys
  8. import typing
  9. from typing import (
  10. TYPE_CHECKING,
  11. Any,
  12. ClassVar,
  13. Dict,
  14. List,
  15. Literal,
  16. NoReturn,
  17. Set,
  18. Tuple,
  19. Type,
  20. TypeVar,
  21. Union,
  22. overload,
  23. )
  24. from reflex import constants
  25. from reflex.constants.base import REFLEX_VAR_OPENING_TAG
  26. from reflex.utils.exceptions import VarTypeError
  27. from reflex.utils.types import GenericType, get_origin
  28. from .base import (
  29. CachedVarOperation,
  30. CustomVarOperationReturn,
  31. LiteralNoneVar,
  32. LiteralVar,
  33. ToOperation,
  34. Var,
  35. VarData,
  36. _global_vars,
  37. cached_property_no_lock,
  38. figure_out_type,
  39. get_unique_variable_name,
  40. unionize,
  41. var_operation,
  42. var_operation_return,
  43. )
  44. from .number import (
  45. BooleanVar,
  46. LiteralNumberVar,
  47. NumberVar,
  48. raise_unsupported_operand_types,
  49. )
  50. if TYPE_CHECKING:
  51. from .object import ObjectVar
  52. class StringVar(Var[str]):
  53. """Base class for immutable string vars."""
  54. @overload
  55. def __add__(self, other: StringVar | str) -> ConcatVarOperation: ...
  56. @overload
  57. def __add__(self, other: NoReturn) -> NoReturn: ...
  58. def __add__(self, other: Any) -> ConcatVarOperation:
  59. """Concatenate two strings.
  60. Args:
  61. other: The other string.
  62. Returns:
  63. The string concatenation operation.
  64. """
  65. if not isinstance(other, (StringVar, str)):
  66. raise_unsupported_operand_types("+", (type(self), type(other)))
  67. return ConcatVarOperation.create(self, other)
  68. @overload
  69. def __radd__(self, other: StringVar | str) -> ConcatVarOperation: ...
  70. @overload
  71. def __radd__(self, other: NoReturn) -> NoReturn: ...
  72. def __radd__(self, other: Any) -> ConcatVarOperation:
  73. """Concatenate two strings.
  74. Args:
  75. other: The other string.
  76. Returns:
  77. The string concatenation operation.
  78. """
  79. if not isinstance(other, (StringVar, str)):
  80. raise_unsupported_operand_types("+", (type(other), type(self)))
  81. return ConcatVarOperation.create(other, self)
  82. @overload
  83. def __mul__(self, other: NumberVar | int) -> StringVar: ...
  84. @overload
  85. def __mul__(self, other: NoReturn) -> NoReturn: ...
  86. def __mul__(self, other: Any) -> StringVar:
  87. """Multiply the sequence by a number or an integer.
  88. Args:
  89. other: The number or integer to multiply the sequence by.
  90. Returns:
  91. StringVar: The resulting sequence after multiplication.
  92. """
  93. if not isinstance(other, (NumberVar, int)):
  94. raise_unsupported_operand_types("*", (type(self), type(other)))
  95. return (self.split() * other).join()
  96. @overload
  97. def __rmul__(self, other: NumberVar | int) -> StringVar: ...
  98. @overload
  99. def __rmul__(self, other: NoReturn) -> NoReturn: ...
  100. def __rmul__(self, other: Any) -> StringVar:
  101. """Multiply the sequence by a number or an integer.
  102. Args:
  103. other: The number or integer to multiply the sequence by.
  104. Returns:
  105. StringVar: The resulting sequence after multiplication.
  106. """
  107. if not isinstance(other, (NumberVar, int)):
  108. raise_unsupported_operand_types("*", (type(other), type(self)))
  109. return (self.split() * other).join()
  110. @overload
  111. def __getitem__(self, i: slice) -> StringVar: ...
  112. @overload
  113. def __getitem__(self, i: int | NumberVar) -> StringVar: ...
  114. def __getitem__(self, i: Any) -> StringVar:
  115. """Get a slice of the string.
  116. Args:
  117. i: The slice.
  118. Returns:
  119. The string slice operation.
  120. """
  121. if isinstance(i, slice):
  122. return self.split()[i].join()
  123. if not isinstance(i, (int, NumberVar)) or (
  124. isinstance(i, NumberVar) and i._is_strict_float()
  125. ):
  126. raise_unsupported_operand_types("[]", (type(self), type(i)))
  127. return string_item_operation(self, i)
  128. def length(self) -> NumberVar:
  129. """Get the length of the string.
  130. Returns:
  131. The string length operation.
  132. """
  133. return self.split().length()
  134. def lower(self) -> StringVar:
  135. """Convert the string to lowercase.
  136. Returns:
  137. The string lower operation.
  138. """
  139. return string_lower_operation(self)
  140. def upper(self) -> StringVar:
  141. """Convert the string to uppercase.
  142. Returns:
  143. The string upper operation.
  144. """
  145. return string_upper_operation(self)
  146. def strip(self) -> StringVar:
  147. """Strip the string.
  148. Returns:
  149. The string strip operation.
  150. """
  151. return string_strip_operation(self)
  152. def bool(self):
  153. """Boolean conversion.
  154. Returns:
  155. The boolean value of the string.
  156. """
  157. return self.length() != 0
  158. def reversed(self) -> StringVar:
  159. """Reverse the string.
  160. Returns:
  161. The string reverse operation.
  162. """
  163. return self.split().reverse().join()
  164. @overload
  165. def contains(
  166. self, other: StringVar | str, field: StringVar | str | None = None
  167. ) -> BooleanVar: ...
  168. @overload
  169. def contains(
  170. self, other: NoReturn, field: StringVar | str | None = None
  171. ) -> NoReturn: ...
  172. def contains(self, other: Any, field: Any = None) -> BooleanVar:
  173. """Check if the string contains another string.
  174. Args:
  175. other: The other string.
  176. field: The field to check.
  177. Returns:
  178. The string contains operation.
  179. """
  180. if not isinstance(other, (StringVar, str)):
  181. raise_unsupported_operand_types("contains", (type(self), type(other)))
  182. if field is not None:
  183. if not isinstance(field, (StringVar, str)):
  184. raise_unsupported_operand_types("contains", (type(self), type(field)))
  185. return string_contains_field_operation(self, other, field)
  186. return string_contains_operation(self, other)
  187. @overload
  188. def split(self, separator: StringVar | str = "") -> ArrayVar[List[str]]: ...
  189. @overload
  190. def split(self, separator: NoReturn) -> NoReturn: ...
  191. def split(self, separator: Any = "") -> ArrayVar[List[str]]:
  192. """Split the string.
  193. Args:
  194. separator: The separator.
  195. Returns:
  196. The string split operation.
  197. """
  198. if not isinstance(separator, (StringVar, str)):
  199. raise_unsupported_operand_types("split", (type(self), type(separator)))
  200. return string_split_operation(self, separator)
  201. @overload
  202. def startswith(self, prefix: StringVar | str) -> BooleanVar: ...
  203. @overload
  204. def startswith(self, prefix: NoReturn) -> NoReturn: ...
  205. def startswith(self, prefix: Any) -> BooleanVar:
  206. """Check if the string starts with a prefix.
  207. Args:
  208. prefix: The prefix.
  209. Returns:
  210. The string starts with operation.
  211. """
  212. if not isinstance(prefix, (StringVar, str)):
  213. raise_unsupported_operand_types("startswith", (type(self), type(prefix)))
  214. return string_starts_with_operation(self, prefix)
  215. @overload
  216. def __lt__(self, other: StringVar | str) -> BooleanVar: ...
  217. @overload
  218. def __lt__(self, other: NoReturn) -> NoReturn: ...
  219. def __lt__(self, other: Any):
  220. """Check if the string is less than another string.
  221. Args:
  222. other: The other string.
  223. Returns:
  224. The string less than operation.
  225. """
  226. if not isinstance(other, (StringVar, str)):
  227. raise_unsupported_operand_types("<", (type(self), type(other)))
  228. return string_lt_operation(self, other)
  229. @overload
  230. def __gt__(self, other: StringVar | str) -> BooleanVar: ...
  231. @overload
  232. def __gt__(self, other: NoReturn) -> NoReturn: ...
  233. def __gt__(self, other: Any):
  234. """Check if the string is greater than another string.
  235. Args:
  236. other: The other string.
  237. Returns:
  238. The string greater than operation.
  239. """
  240. if not isinstance(other, (StringVar, str)):
  241. raise_unsupported_operand_types(">", (type(self), type(other)))
  242. return string_gt_operation(self, other)
  243. @overload
  244. def __le__(self, other: StringVar | str) -> BooleanVar: ...
  245. @overload
  246. def __le__(self, other: NoReturn) -> NoReturn: ...
  247. def __le__(self, other: Any):
  248. """Check if the string is less than or equal to another string.
  249. Args:
  250. other: The other string.
  251. Returns:
  252. The string less than or equal operation.
  253. """
  254. if not isinstance(other, (StringVar, str)):
  255. raise_unsupported_operand_types("<=", (type(self), type(other)))
  256. return string_le_operation(self, other)
  257. @overload
  258. def __ge__(self, other: StringVar | str) -> BooleanVar: ...
  259. @overload
  260. def __ge__(self, other: NoReturn) -> NoReturn: ...
  261. def __ge__(self, other: Any):
  262. """Check if the string is greater than or equal to another string.
  263. Args:
  264. other: The other string.
  265. Returns:
  266. The string greater than or equal operation.
  267. """
  268. if not isinstance(other, (StringVar, str)):
  269. raise_unsupported_operand_types(">=", (type(self), type(other)))
  270. return string_ge_operation(self, other)
  271. @var_operation
  272. def string_lt_operation(lhs: StringVar | str, rhs: StringVar | str):
  273. """Check if a string is less than another string.
  274. Args:
  275. lhs: The left-hand side string.
  276. rhs: The right-hand side string.
  277. Returns:
  278. The string less than operation.
  279. """
  280. return var_operation_return(js_expression=f"{lhs} < {rhs}", var_type=bool)
  281. @var_operation
  282. def string_gt_operation(lhs: StringVar | str, rhs: StringVar | str):
  283. """Check if a string is greater than another string.
  284. Args:
  285. lhs: The left-hand side string.
  286. rhs: The right-hand side string.
  287. Returns:
  288. The string greater than operation.
  289. """
  290. return var_operation_return(js_expression=f"{lhs} > {rhs}", var_type=bool)
  291. @var_operation
  292. def string_le_operation(lhs: StringVar | str, rhs: StringVar | str):
  293. """Check if a string is less than or equal to another string.
  294. Args:
  295. lhs: The left-hand side string.
  296. rhs: The right-hand side string.
  297. Returns:
  298. The string less than or equal operation.
  299. """
  300. return var_operation_return(js_expression=f"{lhs} <= {rhs}", var_type=bool)
  301. @var_operation
  302. def string_ge_operation(lhs: StringVar | str, rhs: StringVar | str):
  303. """Check if a string is greater than or equal to another string.
  304. Args:
  305. lhs: The left-hand side string.
  306. rhs: The right-hand side string.
  307. Returns:
  308. The string greater than or equal operation.
  309. """
  310. return var_operation_return(js_expression=f"{lhs} >= {rhs}", var_type=bool)
  311. @var_operation
  312. def string_lower_operation(string: StringVar):
  313. """Convert a string to lowercase.
  314. Args:
  315. string: The string to convert.
  316. Returns:
  317. The lowercase string.
  318. """
  319. return var_operation_return(js_expression=f"{string}.toLowerCase()", var_type=str)
  320. @var_operation
  321. def string_upper_operation(string: StringVar):
  322. """Convert a string to uppercase.
  323. Args:
  324. string: The string to convert.
  325. Returns:
  326. The uppercase string.
  327. """
  328. return var_operation_return(js_expression=f"{string}.toUpperCase()", var_type=str)
  329. @var_operation
  330. def string_strip_operation(string: StringVar):
  331. """Strip a string.
  332. Args:
  333. string: The string to strip.
  334. Returns:
  335. The stripped string.
  336. """
  337. return var_operation_return(js_expression=f"{string}.trim()", var_type=str)
  338. @var_operation
  339. def string_contains_field_operation(
  340. haystack: StringVar, needle: StringVar | str, field: StringVar | str
  341. ):
  342. """Check if a string contains another string.
  343. Args:
  344. haystack: The haystack.
  345. needle: The needle.
  346. field: The field to check.
  347. Returns:
  348. The string contains operation.
  349. """
  350. return var_operation_return(
  351. js_expression=f"{haystack}.some(obj => obj[{field}] === {needle})",
  352. var_type=bool,
  353. )
  354. @var_operation
  355. def string_contains_operation(haystack: StringVar, needle: StringVar | str):
  356. """Check if a string contains another string.
  357. Args:
  358. haystack: The haystack.
  359. needle: The needle.
  360. Returns:
  361. The string contains operation.
  362. """
  363. return var_operation_return(
  364. js_expression=f"{haystack}.includes({needle})", var_type=bool
  365. )
  366. @var_operation
  367. def string_starts_with_operation(full_string: StringVar, prefix: StringVar | str):
  368. """Check if a string starts with a prefix.
  369. Args:
  370. full_string: The full string.
  371. prefix: The prefix.
  372. Returns:
  373. Whether the string starts with the prefix.
  374. """
  375. return var_operation_return(
  376. js_expression=f"{full_string}.startsWith({prefix})", var_type=bool
  377. )
  378. @var_operation
  379. def string_item_operation(string: StringVar, index: NumberVar | int):
  380. """Get an item from a string.
  381. Args:
  382. string: The string.
  383. index: The index of the item.
  384. Returns:
  385. The item from the string.
  386. """
  387. return var_operation_return(js_expression=f"{string}.at({index})", var_type=str)
  388. @var_operation
  389. def array_join_operation(array: ArrayVar, sep: StringVar | str = ""):
  390. """Join the elements of an array.
  391. Args:
  392. array: The array.
  393. sep: The separator.
  394. Returns:
  395. The joined elements.
  396. """
  397. return var_operation_return(js_expression=f"{array}.join({sep})", var_type=str)
  398. # Compile regex for finding reflex var tags.
  399. _decode_var_pattern_re = (
  400. rf"{constants.REFLEX_VAR_OPENING_TAG}(.*?){constants.REFLEX_VAR_CLOSING_TAG}"
  401. )
  402. _decode_var_pattern = re.compile(_decode_var_pattern_re, flags=re.DOTALL)
  403. @dataclasses.dataclass(
  404. eq=False,
  405. frozen=True,
  406. **{"slots": True} if sys.version_info >= (3, 10) else {},
  407. )
  408. class LiteralStringVar(LiteralVar, StringVar):
  409. """Base class for immutable literal string vars."""
  410. _var_value: str = dataclasses.field(default="")
  411. @classmethod
  412. def create(
  413. cls,
  414. value: str,
  415. _var_type: GenericType | None = str,
  416. _var_data: VarData | None = None,
  417. ) -> StringVar:
  418. """Create a var from a string value.
  419. Args:
  420. value: The value to create the var from.
  421. _var_type: The type of the var.
  422. _var_data: Additional hooks and imports associated with the Var.
  423. Returns:
  424. The var.
  425. """
  426. if REFLEX_VAR_OPENING_TAG in value:
  427. strings_and_vals: list[Var | str] = []
  428. offset = 0
  429. # Find all tags
  430. while m := _decode_var_pattern.search(value):
  431. start, end = m.span()
  432. strings_and_vals.append(value[:start])
  433. serialized_data = m.group(1)
  434. if serialized_data.isnumeric() or (
  435. serialized_data[0] == "-" and serialized_data[1:].isnumeric()
  436. ):
  437. # This is a global immutable var.
  438. var = _global_vars[int(serialized_data)]
  439. strings_and_vals.append(var)
  440. value = value[(end + len(var._js_expr)) :]
  441. offset += end - start
  442. strings_and_vals.append(value)
  443. filtered_strings_and_vals = [
  444. s for s in strings_and_vals if isinstance(s, Var) or s
  445. ]
  446. if len(filtered_strings_and_vals) == 1:
  447. only_string = filtered_strings_and_vals[0]
  448. if isinstance(only_string, str):
  449. return LiteralVar.create(only_string).to(StringVar, _var_type)
  450. else:
  451. return only_string.to(StringVar, only_string._var_type)
  452. concat_result = ConcatVarOperation.create(
  453. *filtered_strings_and_vals,
  454. _var_data=_var_data,
  455. )
  456. return (
  457. concat_result
  458. if _var_type is str
  459. else concat_result.to(StringVar, _var_type)
  460. )
  461. return LiteralStringVar(
  462. _js_expr=json.dumps(value),
  463. _var_type=_var_type,
  464. _var_data=_var_data,
  465. _var_value=value,
  466. )
  467. def __hash__(self) -> int:
  468. """Get the hash of the var.
  469. Returns:
  470. The hash of the var.
  471. """
  472. return hash((self.__class__.__name__, self._var_value))
  473. def json(self) -> str:
  474. """Get the JSON representation of the var.
  475. Returns:
  476. The JSON representation of the var.
  477. """
  478. return json.dumps(self._var_value)
  479. @dataclasses.dataclass(
  480. eq=False,
  481. frozen=True,
  482. **{"slots": True} if sys.version_info >= (3, 10) else {},
  483. )
  484. class ConcatVarOperation(CachedVarOperation, StringVar):
  485. """Representing a concatenation of literal string vars."""
  486. _var_value: Tuple[Var, ...] = dataclasses.field(default_factory=tuple)
  487. @cached_property_no_lock
  488. def _cached_var_name(self) -> str:
  489. """The name of the var.
  490. Returns:
  491. The name of the var.
  492. """
  493. list_of_strs: List[Union[str, Var]] = []
  494. last_string = ""
  495. for var in self._var_value:
  496. if isinstance(var, LiteralStringVar):
  497. last_string += var._var_value
  498. else:
  499. if last_string:
  500. list_of_strs.append(last_string)
  501. last_string = ""
  502. list_of_strs.append(var)
  503. if last_string:
  504. list_of_strs.append(last_string)
  505. list_of_strs_filtered = [
  506. str(LiteralVar.create(s)) for s in list_of_strs if isinstance(s, Var) or s
  507. ]
  508. if len(list_of_strs_filtered) == 1:
  509. return list_of_strs_filtered[0]
  510. return "(" + "+".join(list_of_strs_filtered) + ")"
  511. @cached_property_no_lock
  512. def _cached_get_all_var_data(self) -> VarData | None:
  513. """Get all the VarData asVarDatae Var.
  514. Returns:
  515. The VarData associated with the Var.
  516. """
  517. return VarData.merge(
  518. *[
  519. var._get_all_var_data()
  520. for var in self._var_value
  521. if isinstance(var, Var)
  522. ],
  523. self._var_data,
  524. )
  525. @classmethod
  526. def create(
  527. cls,
  528. *value: Var | str,
  529. _var_data: VarData | None = None,
  530. ) -> ConcatVarOperation:
  531. """Create a var from a string value.
  532. Args:
  533. value: The values to concatenate.
  534. _var_data: Additional hooks and imports associated with the Var.
  535. Returns:
  536. The var.
  537. """
  538. return cls(
  539. _js_expr="",
  540. _var_type=str,
  541. _var_data=_var_data,
  542. _var_value=tuple(map(LiteralVar.create, value)),
  543. )
  544. ARRAY_VAR_TYPE = TypeVar("ARRAY_VAR_TYPE", bound=Union[List, Tuple, Set])
  545. OTHER_TUPLE = TypeVar("OTHER_TUPLE")
  546. INNER_ARRAY_VAR = TypeVar("INNER_ARRAY_VAR")
  547. KEY_TYPE = TypeVar("KEY_TYPE")
  548. VALUE_TYPE = TypeVar("VALUE_TYPE")
  549. class ArrayVar(Var[ARRAY_VAR_TYPE]):
  550. """Base class for immutable array vars."""
  551. @overload
  552. def join(self, sep: StringVar | str = "") -> StringVar: ...
  553. @overload
  554. def join(self, sep: NoReturn) -> NoReturn: ...
  555. def join(self, sep: Any = "") -> StringVar:
  556. """Join the elements of the array.
  557. Args:
  558. sep: The separator between elements.
  559. Returns:
  560. The joined elements.
  561. """
  562. if not isinstance(sep, (StringVar, str)):
  563. raise_unsupported_operand_types("join", (type(self), type(sep)))
  564. return array_join_operation(self, sep)
  565. def reverse(self) -> ArrayVar[ARRAY_VAR_TYPE]:
  566. """Reverse the array.
  567. Returns:
  568. The reversed array.
  569. """
  570. return array_reverse_operation(self)
  571. @overload
  572. def __add__(self, other: ArrayVar[ARRAY_VAR_TYPE]) -> ArrayVar[ARRAY_VAR_TYPE]: ...
  573. @overload
  574. def __add__(self, other: NoReturn) -> NoReturn: ...
  575. def __add__(self, other: Any) -> ArrayVar[ARRAY_VAR_TYPE]:
  576. """Concatenate two arrays.
  577. Parameters:
  578. other: The other array to concatenate.
  579. Returns:
  580. ArrayConcatOperation: The concatenation of the two arrays.
  581. """
  582. if not isinstance(other, ArrayVar):
  583. raise_unsupported_operand_types("+", (type(self), type(other)))
  584. return array_concat_operation(self, other)
  585. @overload
  586. def __getitem__(self, i: slice) -> ArrayVar[ARRAY_VAR_TYPE]: ...
  587. @overload
  588. def __getitem__(
  589. self: (
  590. ArrayVar[Tuple[int, OTHER_TUPLE]]
  591. | ArrayVar[Tuple[float, OTHER_TUPLE]]
  592. | ArrayVar[Tuple[int | float, OTHER_TUPLE]]
  593. ),
  594. i: Literal[0, -2],
  595. ) -> NumberVar: ...
  596. @overload
  597. def __getitem__(
  598. self: (
  599. ArrayVar[Tuple[OTHER_TUPLE, int]]
  600. | ArrayVar[Tuple[OTHER_TUPLE, float]]
  601. | ArrayVar[Tuple[OTHER_TUPLE, int | float]]
  602. ),
  603. i: Literal[1, -1],
  604. ) -> NumberVar: ...
  605. @overload
  606. def __getitem__(
  607. self: ArrayVar[Tuple[str, OTHER_TUPLE]], i: Literal[0, -2]
  608. ) -> StringVar: ...
  609. @overload
  610. def __getitem__(
  611. self: ArrayVar[Tuple[OTHER_TUPLE, str]], i: Literal[1, -1]
  612. ) -> StringVar: ...
  613. @overload
  614. def __getitem__(
  615. self: ArrayVar[Tuple[bool, OTHER_TUPLE]], i: Literal[0, -2]
  616. ) -> BooleanVar: ...
  617. @overload
  618. def __getitem__(
  619. self: ArrayVar[Tuple[OTHER_TUPLE, bool]], i: Literal[1, -1]
  620. ) -> BooleanVar: ...
  621. @overload
  622. def __getitem__(
  623. self: (
  624. ARRAY_VAR_OF_LIST_ELEMENT[int]
  625. | ARRAY_VAR_OF_LIST_ELEMENT[float]
  626. | ARRAY_VAR_OF_LIST_ELEMENT[int | float]
  627. ),
  628. i: int | NumberVar,
  629. ) -> NumberVar: ...
  630. @overload
  631. def __getitem__(
  632. self: ARRAY_VAR_OF_LIST_ELEMENT[str], i: int | NumberVar
  633. ) -> StringVar: ...
  634. @overload
  635. def __getitem__(
  636. self: ARRAY_VAR_OF_LIST_ELEMENT[bool], i: int | NumberVar
  637. ) -> BooleanVar: ...
  638. @overload
  639. def __getitem__(
  640. self: ARRAY_VAR_OF_LIST_ELEMENT[List[INNER_ARRAY_VAR]],
  641. i: int | NumberVar,
  642. ) -> ArrayVar[List[INNER_ARRAY_VAR]]: ...
  643. @overload
  644. def __getitem__(
  645. self: ARRAY_VAR_OF_LIST_ELEMENT[Set[INNER_ARRAY_VAR]],
  646. i: int | NumberVar,
  647. ) -> ArrayVar[Set[INNER_ARRAY_VAR]]: ...
  648. @overload
  649. def __getitem__(
  650. self: ARRAY_VAR_OF_LIST_ELEMENT[Tuple[INNER_ARRAY_VAR, ...]],
  651. i: int | NumberVar,
  652. ) -> ArrayVar[Tuple[INNER_ARRAY_VAR, ...]]: ...
  653. @overload
  654. def __getitem__(
  655. self: ARRAY_VAR_OF_LIST_ELEMENT[Dict[KEY_TYPE, VALUE_TYPE]],
  656. i: int | NumberVar,
  657. ) -> ObjectVar[Dict[KEY_TYPE, VALUE_TYPE]]: ...
  658. @overload
  659. def __getitem__(self, i: int | NumberVar) -> Var: ...
  660. def __getitem__(self, i: Any) -> ArrayVar[ARRAY_VAR_TYPE] | Var:
  661. """Get a slice of the array.
  662. Args:
  663. i: The slice.
  664. Returns:
  665. The array slice operation.
  666. """
  667. if isinstance(i, slice):
  668. return ArraySliceOperation.create(self, i)
  669. if not isinstance(i, (int, NumberVar)) or (
  670. isinstance(i, NumberVar) and i._is_strict_float()
  671. ):
  672. raise_unsupported_operand_types("[]", (type(self), type(i)))
  673. return array_item_operation(self, i)
  674. def length(self) -> NumberVar:
  675. """Get the length of the array.
  676. Returns:
  677. The length of the array.
  678. """
  679. return array_length_operation(self)
  680. @overload
  681. @classmethod
  682. def range(cls, stop: int | NumberVar, /) -> ArrayVar[List[int]]: ...
  683. @overload
  684. @classmethod
  685. def range(
  686. cls,
  687. start: int | NumberVar,
  688. end: int | NumberVar,
  689. step: int | NumberVar = 1,
  690. /,
  691. ) -> ArrayVar[List[int]]: ...
  692. @overload
  693. @classmethod
  694. def range(
  695. cls,
  696. first_endpoint: int | NumberVar,
  697. second_endpoint: int | NumberVar | None = None,
  698. step: int | NumberVar | None = None,
  699. ) -> ArrayVar[List[int]]: ...
  700. @classmethod
  701. def range(
  702. cls,
  703. first_endpoint: int | NumberVar,
  704. second_endpoint: int | NumberVar | None = None,
  705. step: int | NumberVar | None = None,
  706. ) -> ArrayVar[List[int]]:
  707. """Create a range of numbers.
  708. Args:
  709. first_endpoint: The end of the range if second_endpoint is not provided, otherwise the start of the range.
  710. second_endpoint: The end of the range.
  711. step: The step of the range.
  712. Returns:
  713. The range of numbers.
  714. """
  715. if any(
  716. not isinstance(i, (int, NumberVar))
  717. for i in (first_endpoint, second_endpoint, step)
  718. if i is not None
  719. ):
  720. raise_unsupported_operand_types(
  721. "range", (type(first_endpoint), type(second_endpoint), type(step))
  722. )
  723. if second_endpoint is None:
  724. start = 0
  725. end = first_endpoint
  726. else:
  727. start = first_endpoint
  728. end = second_endpoint
  729. return array_range_operation(start, end, step or 1)
  730. @overload
  731. def contains(self, other: Any) -> BooleanVar: ...
  732. @overload
  733. def contains(self, other: Any, field: StringVar | str) -> BooleanVar: ...
  734. def contains(self, other: Any, field: Any = None) -> BooleanVar:
  735. """Check if the array contains an element.
  736. Args:
  737. other: The element to check for.
  738. field: The field to check.
  739. Returns:
  740. The array contains operation.
  741. """
  742. if field is not None:
  743. if not isinstance(field, (StringVar, str)):
  744. raise_unsupported_operand_types("contains", (type(self), type(field)))
  745. return array_contains_field_operation(self, other, field)
  746. return array_contains_operation(self, other)
  747. def pluck(self, field: StringVar | str) -> ArrayVar:
  748. """Pluck a field from the array.
  749. Args:
  750. field: The field to pluck from the array.
  751. Returns:
  752. The array pluck operation.
  753. """
  754. return array_pluck_operation(self, field)
  755. @overload
  756. def __mul__(self, other: NumberVar | int) -> ArrayVar[ARRAY_VAR_TYPE]: ...
  757. @overload
  758. def __mul__(self, other: NoReturn) -> NoReturn: ...
  759. def __mul__(self, other: Any) -> ArrayVar[ARRAY_VAR_TYPE]:
  760. """Multiply the sequence by a number or integer.
  761. Parameters:
  762. other: The number or integer to multiply the sequence by.
  763. Returns:
  764. ArrayVar[ARRAY_VAR_TYPE]: The result of multiplying the sequence by the given number or integer.
  765. """
  766. if not isinstance(other, (NumberVar, int)) or (
  767. isinstance(other, NumberVar) and other._is_strict_float()
  768. ):
  769. raise_unsupported_operand_types("*", (type(self), type(other)))
  770. return repeat_array_operation(self, other)
  771. __rmul__ = __mul__ # type: ignore
  772. @overload
  773. def __lt__(self, other: ArrayVar[ARRAY_VAR_TYPE]) -> BooleanVar: ...
  774. @overload
  775. def __lt__(self, other: list | tuple) -> BooleanVar: ...
  776. def __lt__(self, other: Any):
  777. """Check if the array is less than another array.
  778. Args:
  779. other: The other array.
  780. Returns:
  781. The array less than operation.
  782. """
  783. if not isinstance(other, (ArrayVar, list, tuple)):
  784. raise_unsupported_operand_types("<", (type(self), type(other)))
  785. return array_lt_operation(self, other)
  786. @overload
  787. def __gt__(self, other: ArrayVar[ARRAY_VAR_TYPE]) -> BooleanVar: ...
  788. @overload
  789. def __gt__(self, other: list | tuple) -> BooleanVar: ...
  790. def __gt__(self, other: Any):
  791. """Check if the array is greater than another array.
  792. Args:
  793. other: The other array.
  794. Returns:
  795. The array greater than operation.
  796. """
  797. if not isinstance(other, (ArrayVar, list, tuple)):
  798. raise_unsupported_operand_types(">", (type(self), type(other)))
  799. return array_gt_operation(self, other)
  800. @overload
  801. def __le__(self, other: ArrayVar[ARRAY_VAR_TYPE]) -> BooleanVar: ...
  802. @overload
  803. def __le__(self, other: list | tuple) -> BooleanVar: ...
  804. def __le__(self, other: Any):
  805. """Check if the array is less than or equal to another array.
  806. Args:
  807. other: The other array.
  808. Returns:
  809. The array less than or equal operation.
  810. """
  811. if not isinstance(other, (ArrayVar, list, tuple)):
  812. raise_unsupported_operand_types("<=", (type(self), type(other)))
  813. return array_le_operation(self, other)
  814. @overload
  815. def __ge__(self, other: ArrayVar[ARRAY_VAR_TYPE]) -> BooleanVar: ...
  816. @overload
  817. def __ge__(self, other: list | tuple) -> BooleanVar: ...
  818. def __ge__(self, other: Any):
  819. """Check if the array is greater than or equal to another array.
  820. Args:
  821. other: The other array.
  822. Returns:
  823. The array greater than or equal operation.
  824. """
  825. if not isinstance(other, (ArrayVar, list, tuple)):
  826. raise_unsupported_operand_types(">=", (type(self), type(other)))
  827. return array_ge_operation(self, other)
  828. def foreach(self, fn: Any):
  829. """Apply a function to each element of the array.
  830. Args:
  831. fn: The function to apply.
  832. Returns:
  833. The array after applying the function.
  834. Raises:
  835. VarTypeError: If the function takes more than one argument.
  836. """
  837. from .function import ArgsFunctionOperation
  838. if not callable(fn):
  839. raise_unsupported_operand_types("foreach", (type(self), type(fn)))
  840. # get the number of arguments of the function
  841. num_args = len(inspect.signature(fn).parameters)
  842. if num_args > 1:
  843. raise VarTypeError(
  844. "The function passed to foreach should take at most one argument."
  845. )
  846. if num_args == 0:
  847. return_value = fn()
  848. function_var = ArgsFunctionOperation.create(tuple(), return_value)
  849. else:
  850. # generic number var
  851. number_var = Var("").to(NumberVar)
  852. first_arg_type = self[number_var]._var_type
  853. arg_name = get_unique_variable_name()
  854. # get first argument type
  855. first_arg = Var(
  856. _js_expr=arg_name,
  857. _var_type=first_arg_type,
  858. ).guess_type()
  859. function_var = ArgsFunctionOperation.create((arg_name,), fn(first_arg))
  860. return map_array_operation(self, function_var)
  861. LIST_ELEMENT = TypeVar("LIST_ELEMENT")
  862. ARRAY_VAR_OF_LIST_ELEMENT = Union[
  863. ArrayVar[List[LIST_ELEMENT]],
  864. ArrayVar[Set[LIST_ELEMENT]],
  865. ArrayVar[Tuple[LIST_ELEMENT, ...]],
  866. ]
  867. @dataclasses.dataclass(
  868. eq=False,
  869. frozen=True,
  870. **{"slots": True} if sys.version_info >= (3, 10) else {},
  871. )
  872. class LiteralArrayVar(CachedVarOperation, LiteralVar, ArrayVar[ARRAY_VAR_TYPE]):
  873. """Base class for immutable literal array vars."""
  874. _var_value: Union[
  875. List[Union[Var, Any]],
  876. Set[Union[Var, Any]],
  877. Tuple[Union[Var, Any], ...],
  878. ] = dataclasses.field(default_factory=list)
  879. @cached_property_no_lock
  880. def _cached_var_name(self) -> str:
  881. """The name of the var.
  882. Returns:
  883. The name of the var.
  884. """
  885. return (
  886. "["
  887. + ", ".join(
  888. [str(LiteralVar.create(element)) for element in self._var_value]
  889. )
  890. + "]"
  891. )
  892. @cached_property_no_lock
  893. def _cached_get_all_var_data(self) -> VarData | None:
  894. """Get all the VarData associated with the Var.
  895. Returns:
  896. The VarData associated with the Var.
  897. """
  898. return VarData.merge(
  899. *[
  900. LiteralVar.create(element)._get_all_var_data()
  901. for element in self._var_value
  902. ],
  903. self._var_data,
  904. )
  905. def __hash__(self) -> int:
  906. """Get the hash of the var.
  907. Returns:
  908. The hash of the var.
  909. """
  910. return hash((self.__class__.__name__, self._js_expr))
  911. def json(self) -> str:
  912. """Get the JSON representation of the var.
  913. Returns:
  914. The JSON representation of the var.
  915. """
  916. return (
  917. "["
  918. + ", ".join(
  919. [LiteralVar.create(element).json() for element in self._var_value]
  920. )
  921. + "]"
  922. )
  923. @classmethod
  924. def create(
  925. cls,
  926. value: ARRAY_VAR_TYPE,
  927. _var_type: Type[ARRAY_VAR_TYPE] | None = None,
  928. _var_data: VarData | None = None,
  929. ) -> LiteralArrayVar[ARRAY_VAR_TYPE]:
  930. """Create a var from a string value.
  931. Args:
  932. value: The value to create the var from.
  933. _var_data: Additional hooks and imports associated with the Var.
  934. Returns:
  935. The var.
  936. """
  937. return cls(
  938. _js_expr="",
  939. _var_type=figure_out_type(value) if _var_type is None else _var_type,
  940. _var_data=_var_data,
  941. _var_value=value,
  942. )
  943. @var_operation
  944. def string_split_operation(string: StringVar, sep: StringVar | str = ""):
  945. """Split a string.
  946. Args:
  947. string: The string to split.
  948. sep: The separator.
  949. Returns:
  950. The split string.
  951. """
  952. return var_operation_return(
  953. js_expression=f"{string}.split({sep})", var_type=List[str]
  954. )
  955. @dataclasses.dataclass(
  956. eq=False,
  957. frozen=True,
  958. **{"slots": True} if sys.version_info >= (3, 10) else {},
  959. )
  960. class ArraySliceOperation(CachedVarOperation, ArrayVar):
  961. """Base class for immutable string vars that are the result of a string slice operation."""
  962. _array: ArrayVar = dataclasses.field(
  963. default_factory=lambda: LiteralArrayVar.create([])
  964. )
  965. _start: NumberVar | int = dataclasses.field(default_factory=lambda: 0)
  966. _stop: NumberVar | int = dataclasses.field(default_factory=lambda: 0)
  967. _step: NumberVar | int = dataclasses.field(default_factory=lambda: 1)
  968. @cached_property_no_lock
  969. def _cached_var_name(self) -> str:
  970. """The name of the var.
  971. Returns:
  972. The name of the var.
  973. Raises:
  974. ValueError: If the slice step is zero.
  975. """
  976. start, end, step = self._start, self._stop, self._step
  977. normalized_start = (
  978. LiteralVar.create(start) if start is not None else Var(_js_expr="undefined")
  979. )
  980. normalized_end = (
  981. LiteralVar.create(end) if end is not None else Var(_js_expr="undefined")
  982. )
  983. if step is None:
  984. return f"{str(self._array)}.slice({str(normalized_start)}, {str(normalized_end)})"
  985. if not isinstance(step, Var):
  986. if step < 0:
  987. actual_start = end + 1 if end is not None else 0
  988. actual_end = start + 1 if start is not None else self._array.length()
  989. return str(self._array[actual_start:actual_end].reverse()[::-step])
  990. if step == 0:
  991. raise ValueError("slice step cannot be zero")
  992. return f"{str(self._array)}.slice({str(normalized_start)}, {str(normalized_end)}).filter((_, i) => i % {str(step)} === 0)"
  993. actual_start_reverse = end + 1 if end is not None else 0
  994. actual_end_reverse = start + 1 if start is not None else self._array.length()
  995. 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)"
  996. @classmethod
  997. def create(
  998. cls,
  999. array: ArrayVar,
  1000. slice: slice,
  1001. _var_data: VarData | None = None,
  1002. ) -> ArraySliceOperation:
  1003. """Create a var from a string value.
  1004. Args:
  1005. array: The array.
  1006. slice: The slice.
  1007. _var_data: Additional hooks and imports associated with the Var.
  1008. Returns:
  1009. The var.
  1010. """
  1011. return cls(
  1012. _js_expr="",
  1013. _var_type=array._var_type,
  1014. _var_data=_var_data,
  1015. _array=array,
  1016. _start=slice.start,
  1017. _stop=slice.stop,
  1018. _step=slice.step,
  1019. )
  1020. @var_operation
  1021. def array_pluck_operation(
  1022. array: ArrayVar[ARRAY_VAR_TYPE],
  1023. field: StringVar | str,
  1024. ) -> CustomVarOperationReturn[ARRAY_VAR_TYPE]:
  1025. """Pluck a field from an array of objects.
  1026. Args:
  1027. array: The array to pluck from.
  1028. field: The field to pluck from the objects in the array.
  1029. Returns:
  1030. The reversed array.
  1031. """
  1032. return var_operation_return(
  1033. js_expression=f"{array}.map(e=>e?.[{field}])",
  1034. var_type=array._var_type,
  1035. )
  1036. @var_operation
  1037. def array_reverse_operation(
  1038. array: ArrayVar[ARRAY_VAR_TYPE],
  1039. ) -> CustomVarOperationReturn[ARRAY_VAR_TYPE]:
  1040. """Reverse an array.
  1041. Args:
  1042. array: The array to reverse.
  1043. Returns:
  1044. The reversed array.
  1045. """
  1046. return var_operation_return(
  1047. js_expression=f"{array}.slice().reverse()",
  1048. var_type=array._var_type,
  1049. )
  1050. @var_operation
  1051. def array_lt_operation(lhs: ArrayVar | list | tuple, rhs: ArrayVar | list | tuple):
  1052. """Check if an array is less than another array.
  1053. Args:
  1054. lhs: The left-hand side array.
  1055. rhs: The right-hand side array.
  1056. Returns:
  1057. The array less than operation.
  1058. """
  1059. return var_operation_return(js_expression=f"{lhs} < {rhs}", var_type=bool)
  1060. @var_operation
  1061. def array_gt_operation(lhs: ArrayVar | list | tuple, rhs: ArrayVar | list | tuple):
  1062. """Check if an array is greater than another array.
  1063. Args:
  1064. lhs: The left-hand side array.
  1065. rhs: The right-hand side array.
  1066. Returns:
  1067. The array greater than operation.
  1068. """
  1069. return var_operation_return(js_expression=f"{lhs} > {rhs}", var_type=bool)
  1070. @var_operation
  1071. def array_le_operation(lhs: ArrayVar | list | tuple, rhs: ArrayVar | list | tuple):
  1072. """Check if an array is less than or equal to another array.
  1073. Args:
  1074. lhs: The left-hand side array.
  1075. rhs: The right-hand side array.
  1076. Returns:
  1077. The array less than or equal operation.
  1078. """
  1079. return var_operation_return(js_expression=f"{lhs} <= {rhs}", var_type=bool)
  1080. @var_operation
  1081. def array_ge_operation(lhs: ArrayVar | list | tuple, rhs: ArrayVar | list | tuple):
  1082. """Check if an array is greater than or equal to another array.
  1083. Args:
  1084. lhs: The left-hand side array.
  1085. rhs: The right-hand side array.
  1086. Returns:
  1087. The array greater than or equal operation.
  1088. """
  1089. return var_operation_return(js_expression=f"{lhs} >= {rhs}", var_type=bool)
  1090. @var_operation
  1091. def array_length_operation(array: ArrayVar):
  1092. """Get the length of an array.
  1093. Args:
  1094. array: The array.
  1095. Returns:
  1096. The length of the array.
  1097. """
  1098. return var_operation_return(
  1099. js_expression=f"{array}.length",
  1100. var_type=int,
  1101. )
  1102. def is_tuple_type(t: GenericType) -> bool:
  1103. """Check if a type is a tuple type.
  1104. Args:
  1105. t: The type to check.
  1106. Returns:
  1107. Whether the type is a tuple type.
  1108. """
  1109. if inspect.isclass(t):
  1110. return issubclass(t, tuple)
  1111. return get_origin(t) is tuple
  1112. @var_operation
  1113. def array_item_operation(array: ArrayVar, index: NumberVar | int):
  1114. """Get an item from an array.
  1115. Args:
  1116. array: The array.
  1117. index: The index of the item.
  1118. Returns:
  1119. The item from the array.
  1120. """
  1121. args = typing.get_args(array._var_type)
  1122. if args and isinstance(index, LiteralNumberVar) and is_tuple_type(array._var_type):
  1123. index_value = int(index._var_value)
  1124. element_type = args[index_value % len(args)]
  1125. else:
  1126. element_type = unionize(*args)
  1127. return var_operation_return(
  1128. js_expression=f"{str(array)}.at({str(index)})",
  1129. var_type=element_type,
  1130. )
  1131. @var_operation
  1132. def array_range_operation(
  1133. start: NumberVar | int, stop: NumberVar | int, step: NumberVar | int
  1134. ):
  1135. """Create a range of numbers.
  1136. Args:
  1137. start: The start of the range.
  1138. stop: The end of the range.
  1139. step: The step of the range.
  1140. Returns:
  1141. The range of numbers.
  1142. """
  1143. return var_operation_return(
  1144. js_expression=f"Array.from({{ length: ({str(stop)} - {str(start)}) / {str(step)} }}, (_, i) => {str(start)} + i * {str(step)})",
  1145. var_type=List[int],
  1146. )
  1147. @var_operation
  1148. def array_contains_field_operation(
  1149. haystack: ArrayVar, needle: Any | Var, field: StringVar | str
  1150. ):
  1151. """Check if an array contains an element.
  1152. Args:
  1153. haystack: The array to check.
  1154. needle: The element to check for.
  1155. field: The field to check.
  1156. Returns:
  1157. The array contains operation.
  1158. """
  1159. return var_operation_return(
  1160. js_expression=f"{haystack}.some(obj => obj[{field}] === {needle})",
  1161. var_type=bool,
  1162. )
  1163. @var_operation
  1164. def array_contains_operation(haystack: ArrayVar, needle: Any | Var):
  1165. """Check if an array contains an element.
  1166. Args:
  1167. haystack: The array to check.
  1168. needle: The element to check for.
  1169. Returns:
  1170. The array contains operation.
  1171. """
  1172. return var_operation_return(
  1173. js_expression=f"{haystack}.includes({needle})",
  1174. var_type=bool,
  1175. )
  1176. @dataclasses.dataclass(
  1177. eq=False,
  1178. frozen=True,
  1179. **{"slots": True} if sys.version_info >= (3, 10) else {},
  1180. )
  1181. class ToStringOperation(ToOperation, StringVar):
  1182. """Base class for immutable string vars that are the result of a to string operation."""
  1183. _original: Var = dataclasses.field(default_factory=lambda: LiteralNoneVar.create())
  1184. _default_var_type: ClassVar[Type] = str
  1185. @dataclasses.dataclass(
  1186. eq=False,
  1187. frozen=True,
  1188. **{"slots": True} if sys.version_info >= (3, 10) else {},
  1189. )
  1190. class ToArrayOperation(ToOperation, ArrayVar):
  1191. """Base class for immutable array vars that are the result of a to array operation."""
  1192. _original: Var = dataclasses.field(default_factory=lambda: LiteralNoneVar.create())
  1193. _default_var_type: ClassVar[Type] = List[Any]
  1194. @var_operation
  1195. def repeat_array_operation(
  1196. array: ArrayVar[ARRAY_VAR_TYPE], count: NumberVar | int
  1197. ) -> CustomVarOperationReturn[ARRAY_VAR_TYPE]:
  1198. """Repeat an array a number of times.
  1199. Args:
  1200. array: The array to repeat.
  1201. count: The number of times to repeat the array.
  1202. Returns:
  1203. The repeated array.
  1204. """
  1205. return var_operation_return(
  1206. js_expression=f"Array.from({{ length: {count} }}).flatMap(() => {array})",
  1207. var_type=array._var_type,
  1208. )
  1209. if TYPE_CHECKING:
  1210. from .function import FunctionVar
  1211. @var_operation
  1212. def map_array_operation(
  1213. array: ArrayVar[ARRAY_VAR_TYPE],
  1214. function: FunctionVar,
  1215. ):
  1216. """Map a function over an array.
  1217. Args:
  1218. array: The array.
  1219. function: The function to map.
  1220. Returns:
  1221. The mapped array.
  1222. """
  1223. return var_operation_return(
  1224. js_expression=f"{array}.map({function})", var_type=List[Any]
  1225. )
  1226. @var_operation
  1227. def array_concat_operation(
  1228. lhs: ArrayVar[ARRAY_VAR_TYPE], rhs: ArrayVar[ARRAY_VAR_TYPE]
  1229. ) -> CustomVarOperationReturn[ARRAY_VAR_TYPE]:
  1230. """Concatenate two arrays.
  1231. Args:
  1232. lhs: The left-hand side array.
  1233. rhs: The right-hand side array.
  1234. Returns:
  1235. The concatenated array.
  1236. """
  1237. return var_operation_return(
  1238. js_expression=f"[...{lhs}, ...{rhs}]",
  1239. var_type=Union[lhs._var_type, rhs._var_type],
  1240. )