sequence.py 48 KB

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