sequence.py 51 KB

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