sequence.py 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039
  1. """Collection of string classes and utilities."""
  2. from __future__ import annotations
  3. import dataclasses
  4. import functools
  5. import json
  6. import re
  7. import sys
  8. from functools import cached_property
  9. from typing import Any, List, Set, Tuple, Union
  10. from reflex import constants
  11. from reflex.constants.base import REFLEX_VAR_OPENING_TAG
  12. from reflex.experimental.vars.base import (
  13. ImmutableVar,
  14. LiteralVar,
  15. )
  16. from reflex.experimental.vars.number import BooleanVar, NotEqualOperation, NumberVar
  17. from reflex.vars import ImmutableVarData, Var, VarData, _global_vars
  18. class StringVar(ImmutableVar):
  19. """Base class for immutable string vars."""
  20. def __add__(self, other: StringVar | str) -> ConcatVarOperation:
  21. """Concatenate two strings.
  22. Args:
  23. other: The other string.
  24. Returns:
  25. The string concatenation operation.
  26. """
  27. return ConcatVarOperation(self, other)
  28. def __radd__(self, other: StringVar | str) -> ConcatVarOperation:
  29. """Concatenate two strings.
  30. Args:
  31. other: The other string.
  32. Returns:
  33. The string concatenation operation.
  34. """
  35. return ConcatVarOperation(other, self)
  36. def __mul__(self, other: int) -> ConcatVarOperation:
  37. """Concatenate two strings.
  38. Args:
  39. other: The other string.
  40. Returns:
  41. The string concatenation operation.
  42. """
  43. return ConcatVarOperation(*[self for _ in range(other)])
  44. def __rmul__(self, other: int) -> ConcatVarOperation:
  45. """Concatenate two strings.
  46. Args:
  47. other: The other string.
  48. Returns:
  49. The string concatenation operation.
  50. """
  51. return ConcatVarOperation(*[self for _ in range(other)])
  52. def __getitem__(self, i: slice | int) -> StringSliceOperation | StringItemOperation:
  53. """Get a slice of the string.
  54. Args:
  55. i: The slice.
  56. Returns:
  57. The string slice operation.
  58. """
  59. if isinstance(i, slice):
  60. return StringSliceOperation(self, i)
  61. return StringItemOperation(self, i)
  62. def length(self) -> StringLengthOperation:
  63. """Get the length of the string.
  64. Returns:
  65. The string length operation.
  66. """
  67. return StringLengthOperation(self)
  68. def lower(self) -> StringLowerOperation:
  69. """Convert the string to lowercase.
  70. Returns:
  71. The string lower operation.
  72. """
  73. return StringLowerOperation(self)
  74. def upper(self) -> StringUpperOperation:
  75. """Convert the string to uppercase.
  76. Returns:
  77. The string upper operation.
  78. """
  79. return StringUpperOperation(self)
  80. def strip(self) -> StringStripOperation:
  81. """Strip the string.
  82. Returns:
  83. The string strip operation.
  84. """
  85. return StringStripOperation(self)
  86. def bool(self) -> NotEqualOperation:
  87. """Boolean conversion.
  88. Returns:
  89. The boolean value of the string.
  90. """
  91. return NotEqualOperation(self.length(), 0)
  92. def reversed(self) -> StringReverseOperation:
  93. """Reverse the string.
  94. Returns:
  95. The string reverse operation.
  96. """
  97. return StringReverseOperation(self)
  98. def contains(self, other: StringVar | str) -> StringContainsOperation:
  99. """Check if the string contains another string.
  100. Args:
  101. other: The other string.
  102. Returns:
  103. The string contains operation.
  104. """
  105. return StringContainsOperation(self, other)
  106. def split(self, separator: StringVar | str = "") -> StringSplitOperation:
  107. """Split the string.
  108. Args:
  109. separator: The separator.
  110. Returns:
  111. The string split operation.
  112. """
  113. return StringSplitOperation(self, separator)
  114. @dataclasses.dataclass(
  115. eq=False,
  116. frozen=True,
  117. **{"slots": True} if sys.version_info >= (3, 10) else {},
  118. )
  119. class StringToNumberOperation(NumberVar):
  120. """Base class for immutable number vars that are the result of a string to number operation."""
  121. a: StringVar = dataclasses.field(
  122. default_factory=lambda: LiteralStringVar.create("")
  123. )
  124. def __init__(self, a: StringVar | str, _var_data: VarData | None = None):
  125. """Initialize the string to number operation var.
  126. Args:
  127. a: The string.
  128. _var_data: Additional hooks and imports associated with the Var.
  129. """
  130. super(StringToNumberOperation, self).__init__(
  131. _var_name="",
  132. _var_type=float,
  133. _var_data=ImmutableVarData.merge(_var_data),
  134. )
  135. object.__setattr__(
  136. self, "a", a if isinstance(a, Var) else LiteralStringVar.create(a)
  137. )
  138. object.__delattr__(self, "_var_name")
  139. @cached_property
  140. def _cached_var_name(self) -> str:
  141. """The name of the var.
  142. Raises:
  143. NotImplementedError: Must be implemented by subclasses.
  144. """
  145. raise NotImplementedError(
  146. "StringToNumberOperation must implement _cached_var_name"
  147. )
  148. def __getattr__(self, name: str) -> Any:
  149. """Get an attribute of the var.
  150. Args:
  151. name: The name of the attribute.
  152. Returns:
  153. The attribute value.
  154. """
  155. if name == "_var_name":
  156. return self._cached_var_name
  157. getattr(super(StringToNumberOperation, self), name)
  158. @cached_property
  159. def _cached_get_all_var_data(self) -> ImmutableVarData | None:
  160. """Get all VarData associated with the Var.
  161. Returns:
  162. The VarData of the components and all of its children.
  163. """
  164. return ImmutableVarData.merge(self.a._get_all_var_data(), self._var_data)
  165. def _get_all_var_data(self) -> ImmutableVarData | None:
  166. return self._cached_get_all_var_data
  167. class StringLengthOperation(StringToNumberOperation):
  168. """Base class for immutable number vars that are the result of a string length operation."""
  169. @cached_property
  170. def _cached_var_name(self) -> str:
  171. """The name of the var.
  172. Returns:
  173. The name of the var.
  174. """
  175. return f"{str(self.a)}.length"
  176. @dataclasses.dataclass(
  177. eq=False,
  178. frozen=True,
  179. **{"slots": True} if sys.version_info >= (3, 10) else {},
  180. )
  181. class StringToStringOperation(StringVar):
  182. """Base class for immutable string vars that are the result of a string to string operation."""
  183. a: StringVar = dataclasses.field(
  184. default_factory=lambda: LiteralStringVar.create("")
  185. )
  186. def __init__(self, a: StringVar | str, _var_data: VarData | None = None):
  187. """Initialize the string to string operation var.
  188. Args:
  189. a: The string.
  190. _var_data: Additional hooks and imports associated with the Var.
  191. """
  192. super(StringToStringOperation, self).__init__(
  193. _var_name="",
  194. _var_type=str,
  195. _var_data=ImmutableVarData.merge(_var_data),
  196. )
  197. object.__setattr__(
  198. self, "a", a if isinstance(a, Var) else LiteralStringVar.create(a)
  199. )
  200. object.__delattr__(self, "_var_name")
  201. @cached_property
  202. def _cached_var_name(self) -> str:
  203. """The name of the var.
  204. Raises:
  205. NotImplementedError: Must be implemented by subclasses.
  206. """
  207. raise NotImplementedError(
  208. "StringToStringOperation must implement _cached_var_name"
  209. )
  210. def __getattr__(self, name: str) -> Any:
  211. """Get an attribute of the var.
  212. Args:
  213. name: The name of the attribute.
  214. Returns:
  215. The attribute value.
  216. """
  217. if name == "_var_name":
  218. return self._cached_var_name
  219. getattr(super(StringToStringOperation, self), name)
  220. @cached_property
  221. def _cached_get_all_var_data(self) -> ImmutableVarData | None:
  222. """Get all VarData associated with the Var.
  223. Returns:
  224. The VarData of the components and all of its children.
  225. """
  226. return ImmutableVarData.merge(
  227. self.a._get_all_var_data() if isinstance(self.a, Var) else None,
  228. self._var_data,
  229. )
  230. def _get_all_var_data(self) -> ImmutableVarData | None:
  231. return self._cached_get_all_var_data
  232. class StringLowerOperation(StringToStringOperation):
  233. """Base class for immutable string vars that are the result of a string lower operation."""
  234. @cached_property
  235. def _cached_var_name(self) -> str:
  236. """The name of the var.
  237. Returns:
  238. The name of the var.
  239. """
  240. return f"{str(self.a)}.toLowerCase()"
  241. class StringUpperOperation(StringToStringOperation):
  242. """Base class for immutable string vars that are the result of a string upper operation."""
  243. @cached_property
  244. def _cached_var_name(self) -> str:
  245. """The name of the var.
  246. Returns:
  247. The name of the var.
  248. """
  249. return f"{str(self.a)}.toUpperCase()"
  250. class StringStripOperation(StringToStringOperation):
  251. """Base class for immutable string vars that are the result of a string strip operation."""
  252. @cached_property
  253. def _cached_var_name(self) -> str:
  254. """The name of the var.
  255. Returns:
  256. The name of the var.
  257. """
  258. return f"{str(self.a)}.trim()"
  259. class StringReverseOperation(StringToStringOperation):
  260. """Base class for immutable string vars that are the result of a string reverse operation."""
  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)}.split('').reverse().join('')"
  268. @dataclasses.dataclass(
  269. eq=False,
  270. frozen=True,
  271. **{"slots": True} if sys.version_info >= (3, 10) else {},
  272. )
  273. class StringContainsOperation(BooleanVar):
  274. """Base class for immutable boolean vars that are the result of a string contains operation."""
  275. a: StringVar = dataclasses.field(
  276. default_factory=lambda: LiteralStringVar.create("")
  277. )
  278. b: StringVar = dataclasses.field(
  279. default_factory=lambda: LiteralStringVar.create("")
  280. )
  281. def __init__(
  282. self, a: StringVar | str, b: StringVar | str, _var_data: VarData | None = None
  283. ):
  284. """Initialize the string contains operation var.
  285. Args:
  286. a: The first string.
  287. b: The second string.
  288. _var_data: Additional hooks and imports associated with the Var.
  289. """
  290. super(StringContainsOperation, self).__init__(
  291. _var_name="",
  292. _var_type=bool,
  293. _var_data=ImmutableVarData.merge(_var_data),
  294. )
  295. object.__setattr__(
  296. self, "a", a if isinstance(a, Var) else LiteralStringVar.create(a)
  297. )
  298. object.__setattr__(
  299. self, "b", b if isinstance(b, Var) else LiteralStringVar.create(b)
  300. )
  301. object.__delattr__(self, "_var_name")
  302. @cached_property
  303. def _cached_var_name(self) -> str:
  304. """The name of the var.
  305. Returns:
  306. The name of the var.
  307. """
  308. return f"{str(self.a)}.includes({str(self.b)})"
  309. def __getattr__(self, name: str) -> Any:
  310. """Get an attribute of the var.
  311. Args:
  312. name: The name of the attribute.
  313. Returns:
  314. The attribute value.
  315. """
  316. if name == "_var_name":
  317. return self._cached_var_name
  318. getattr(super(StringContainsOperation, self), name)
  319. @cached_property
  320. def _cached_get_all_var_data(self) -> ImmutableVarData | None:
  321. """Get all VarData associated with the Var.
  322. Returns:
  323. The VarData of the components and all of its children.
  324. """
  325. return ImmutableVarData.merge(
  326. self.a._get_all_var_data(), self.b._get_all_var_data(), self._var_data
  327. )
  328. def _get_all_var_data(self) -> ImmutableVarData | None:
  329. return self._cached_get_all_var_data
  330. @dataclasses.dataclass(
  331. eq=False,
  332. frozen=True,
  333. **{"slots": True} if sys.version_info >= (3, 10) else {},
  334. )
  335. class StringSliceOperation(StringVar):
  336. """Base class for immutable string vars that are the result of a string slice operation."""
  337. a: StringVar = dataclasses.field(
  338. default_factory=lambda: LiteralStringVar.create("")
  339. )
  340. _slice: slice = dataclasses.field(default_factory=lambda: slice(None, None, None))
  341. def __init__(
  342. self, a: StringVar | str, _slice: slice, _var_data: VarData | None = None
  343. ):
  344. """Initialize the string slice operation var.
  345. Args:
  346. a: The string.
  347. _slice: The slice.
  348. _var_data: Additional hooks and imports associated with the Var.
  349. """
  350. super(StringSliceOperation, self).__init__(
  351. _var_name="",
  352. _var_type=str,
  353. _var_data=ImmutableVarData.merge(_var_data),
  354. )
  355. object.__setattr__(
  356. self, "a", a if isinstance(a, Var) else LiteralStringVar.create(a)
  357. )
  358. object.__setattr__(self, "_slice", _slice)
  359. object.__delattr__(self, "_var_name")
  360. @cached_property
  361. def _cached_var_name(self) -> str:
  362. """The name of the var.
  363. Returns:
  364. The name of the var.
  365. Raises:
  366. ValueError: If the slice step is zero.
  367. """
  368. start, end, step = self._slice.start, self._slice.stop, self._slice.step
  369. if step is not None and step < 0:
  370. actual_start = end + 1 if end is not None else 0
  371. actual_end = start + 1 if start is not None else self.a.length()
  372. return str(
  373. StringSliceOperation(
  374. StringReverseOperation(
  375. StringSliceOperation(self.a, slice(actual_start, actual_end))
  376. ),
  377. slice(None, None, -step),
  378. )
  379. )
  380. start = (
  381. LiteralVar.create(start)
  382. if start is not None
  383. else ImmutableVar.create_safe("undefined")
  384. )
  385. end = (
  386. LiteralVar.create(end)
  387. if end is not None
  388. else ImmutableVar.create_safe("undefined")
  389. )
  390. if step is None:
  391. return f"{str(self.a)}.slice({str(start)}, {str(end)})"
  392. if step == 0:
  393. raise ValueError("slice step cannot be zero")
  394. return f"{str(self.a)}.slice({str(start)}, {str(end)}).split('').filter((_, i) => i % {str(step)} === 0).join('')"
  395. def __getattr__(self, name: str) -> Any:
  396. """Get an attribute of the var.
  397. Args:
  398. name: The name of the attribute.
  399. Returns:
  400. The attribute value.
  401. """
  402. if name == "_var_name":
  403. return self._cached_var_name
  404. getattr(super(StringSliceOperation, self), name)
  405. @cached_property
  406. def _cached_get_all_var_data(self) -> ImmutableVarData | None:
  407. """Get all VarData associated with the Var.
  408. Returns:
  409. The VarData of the components and all of its children.
  410. """
  411. return ImmutableVarData.merge(
  412. self.a._get_all_var_data(),
  413. self.start._get_all_var_data(),
  414. self.end._get_all_var_data(),
  415. self._var_data,
  416. )
  417. def _get_all_var_data(self) -> ImmutableVarData | None:
  418. return self._cached_get_all_var_data
  419. @dataclasses.dataclass(
  420. eq=False,
  421. frozen=True,
  422. **{"slots": True} if sys.version_info >= (3, 10) else {},
  423. )
  424. class StringItemOperation(StringVar):
  425. """Base class for immutable string vars that are the result of a string item operation."""
  426. a: StringVar = dataclasses.field(
  427. default_factory=lambda: LiteralStringVar.create("")
  428. )
  429. i: int = dataclasses.field(default=0)
  430. def __init__(self, a: StringVar | str, i: int, _var_data: VarData | None = None):
  431. """Initialize the string item operation var.
  432. Args:
  433. a: The string.
  434. i: The index.
  435. _var_data: Additional hooks and imports associated with the Var.
  436. """
  437. super(StringItemOperation, self).__init__(
  438. _var_name="",
  439. _var_type=str,
  440. _var_data=ImmutableVarData.merge(_var_data),
  441. )
  442. object.__setattr__(
  443. self, "a", a if isinstance(a, Var) else LiteralStringVar.create(a)
  444. )
  445. object.__setattr__(self, "i", i)
  446. object.__delattr__(self, "_var_name")
  447. @cached_property
  448. def _cached_var_name(self) -> str:
  449. """The name of the var.
  450. Returns:
  451. The name of the var.
  452. """
  453. return f"{str(self.a)}.at({str(self.i)})"
  454. def __getattr__(self, name: str) -> Any:
  455. """Get an attribute of the var.
  456. Args:
  457. name: The name of the attribute.
  458. Returns:
  459. The attribute value.
  460. """
  461. if name == "_var_name":
  462. return self._cached_var_name
  463. getattr(super(StringItemOperation, self), name)
  464. @cached_property
  465. def _cached_get_all_var_data(self) -> ImmutableVarData | None:
  466. """Get all VarData associated with the Var.
  467. Returns:
  468. The VarData of the components and all of its children.
  469. """
  470. return ImmutableVarData.merge(self.a._get_all_var_data(), self._var_data)
  471. def _get_all_var_data(self) -> ImmutableVarData | None:
  472. return self._cached_get_all_var_data
  473. class ArrayJoinOperation(StringVar):
  474. """Base class for immutable string vars that are the result of an array join operation."""
  475. a: ArrayVar = dataclasses.field(default_factory=lambda: LiteralArrayVar([]))
  476. b: StringVar = dataclasses.field(
  477. default_factory=lambda: LiteralStringVar.create("")
  478. )
  479. def __init__(
  480. self, a: ArrayVar | list, b: StringVar | str, _var_data: VarData | None = None
  481. ):
  482. """Initialize the array join operation var.
  483. Args:
  484. a: The array.
  485. b: The separator.
  486. _var_data: Additional hooks and imports associated with the Var.
  487. """
  488. super(ArrayJoinOperation, self).__init__(
  489. _var_name="",
  490. _var_type=str,
  491. _var_data=ImmutableVarData.merge(_var_data),
  492. )
  493. object.__setattr__(
  494. self, "a", a if isinstance(a, Var) else LiteralArrayVar.create(a)
  495. )
  496. object.__setattr__(
  497. self, "b", b if isinstance(b, Var) else LiteralStringVar.create(b)
  498. )
  499. object.__delattr__(self, "_var_name")
  500. @cached_property
  501. def _cached_var_name(self) -> str:
  502. """The name of the var.
  503. Returns:
  504. The name of the var.
  505. """
  506. return f"{str(self.a)}.join({str(self.b)})"
  507. def __getattr__(self, name: str) -> Any:
  508. """Get an attribute of the var.
  509. Args:
  510. name: The name of the attribute.
  511. Returns:
  512. The attribute value.
  513. """
  514. if name == "_var_name":
  515. return self._cached_var_name
  516. getattr(super(ArrayJoinOperation, self), name)
  517. @cached_property
  518. def _cached_get_all_var_data(self) -> ImmutableVarData | None:
  519. """Get all VarData associated with the Var.
  520. Returns:
  521. The VarData of the components and all of its children.
  522. """
  523. return ImmutableVarData.merge(
  524. self.a._get_all_var_data(), self.b._get_all_var_data(), self._var_data
  525. )
  526. def _get_all_var_data(self) -> ImmutableVarData | None:
  527. return self._cached_get_all_var_data
  528. # Compile regex for finding reflex var tags.
  529. _decode_var_pattern_re = (
  530. rf"{constants.REFLEX_VAR_OPENING_TAG}(.*?){constants.REFLEX_VAR_CLOSING_TAG}"
  531. )
  532. _decode_var_pattern = re.compile(_decode_var_pattern_re, flags=re.DOTALL)
  533. @dataclasses.dataclass(
  534. eq=False,
  535. frozen=True,
  536. **{"slots": True} if sys.version_info >= (3, 10) else {},
  537. )
  538. class LiteralStringVar(LiteralVar, StringVar):
  539. """Base class for immutable literal string vars."""
  540. _var_value: str = dataclasses.field(default="")
  541. def __init__(
  542. self,
  543. _var_value: str,
  544. _var_data: VarData | None = None,
  545. ):
  546. """Initialize the string var.
  547. Args:
  548. _var_value: The value of the var.
  549. _var_data: Additional hooks and imports associated with the Var.
  550. """
  551. super(LiteralStringVar, self).__init__(
  552. _var_name=f'"{_var_value}"',
  553. _var_type=str,
  554. _var_data=ImmutableVarData.merge(_var_data),
  555. )
  556. object.__setattr__(self, "_var_value", _var_value)
  557. @classmethod
  558. def create(
  559. cls,
  560. value: str,
  561. _var_data: VarData | None = None,
  562. ) -> LiteralStringVar | ConcatVarOperation:
  563. """Create a var from a string value.
  564. Args:
  565. value: The value to create the var from.
  566. _var_data: Additional hooks and imports associated with the Var.
  567. Returns:
  568. The var.
  569. """
  570. if REFLEX_VAR_OPENING_TAG in value:
  571. strings_and_vals: list[Var | str] = []
  572. offset = 0
  573. # Initialize some methods for reading json.
  574. var_data_config = VarData().__config__
  575. def json_loads(s):
  576. try:
  577. return var_data_config.json_loads(s)
  578. except json.decoder.JSONDecodeError:
  579. return var_data_config.json_loads(
  580. var_data_config.json_loads(f'"{s}"')
  581. )
  582. # Find all tags
  583. while m := _decode_var_pattern.search(value):
  584. start, end = m.span()
  585. if start > 0:
  586. strings_and_vals.append(value[:start])
  587. serialized_data = m.group(1)
  588. if serialized_data.isnumeric() or (
  589. serialized_data[0] == "-" and serialized_data[1:].isnumeric()
  590. ):
  591. # This is a global immutable var.
  592. var = _global_vars[int(serialized_data)]
  593. strings_and_vals.append(var)
  594. value = value[(end + len(var._var_name)) :]
  595. else:
  596. data = json_loads(serialized_data)
  597. string_length = data.pop("string_length", None)
  598. var_data = VarData.parse_obj(data)
  599. # Use string length to compute positions of interpolations.
  600. if string_length is not None:
  601. realstart = start + offset
  602. var_data.interpolations = [
  603. (realstart, realstart + string_length)
  604. ]
  605. strings_and_vals.append(
  606. ImmutableVar.create_safe(
  607. value[end : (end + string_length)], _var_data=var_data
  608. )
  609. )
  610. value = value[(end + string_length) :]
  611. offset += end - start
  612. if value:
  613. strings_and_vals.append(value)
  614. return ConcatVarOperation(*strings_and_vals, _var_data=_var_data)
  615. return LiteralStringVar(
  616. value,
  617. _var_data=_var_data,
  618. )
  619. @dataclasses.dataclass(
  620. eq=False,
  621. frozen=True,
  622. **{"slots": True} if sys.version_info >= (3, 10) else {},
  623. )
  624. class ConcatVarOperation(StringVar):
  625. """Representing a concatenation of literal string vars."""
  626. _var_value: Tuple[Union[Var, str], ...] = dataclasses.field(default_factory=tuple)
  627. def __init__(self, *value: Var | str, _var_data: VarData | None = None):
  628. """Initialize the operation of concatenating literal string vars.
  629. Args:
  630. value: The values to concatenate.
  631. _var_data: Additional hooks and imports associated with the Var.
  632. """
  633. super(ConcatVarOperation, self).__init__(
  634. _var_name="", _var_data=ImmutableVarData.merge(_var_data), _var_type=str
  635. )
  636. object.__setattr__(self, "_var_value", value)
  637. object.__delattr__(self, "_var_name")
  638. def __getattr__(self, name):
  639. """Get an attribute of the var.
  640. Args:
  641. name: The name of the attribute.
  642. Returns:
  643. The attribute of the var.
  644. """
  645. if name == "_var_name":
  646. return self._cached_var_name
  647. return super(type(self), self).__getattr__(name)
  648. @cached_property
  649. def _cached_var_name(self) -> str:
  650. """The name of the var.
  651. Returns:
  652. The name of the var.
  653. """
  654. return (
  655. "("
  656. + "+".join(
  657. [
  658. str(element) if isinstance(element, Var) else f'"{element}"'
  659. for element in self._var_value
  660. ]
  661. )
  662. + ")"
  663. )
  664. @cached_property
  665. def _cached_get_all_var_data(self) -> ImmutableVarData | None:
  666. """Get all VarData associated with the Var.
  667. Returns:
  668. The VarData of the components and all of its children.
  669. """
  670. return ImmutableVarData.merge(
  671. *[
  672. var._get_all_var_data()
  673. for var in self._var_value
  674. if isinstance(var, Var)
  675. ],
  676. self._var_data,
  677. )
  678. def _get_all_var_data(self) -> ImmutableVarData | None:
  679. """Wrapper method for cached property.
  680. Returns:
  681. The VarData of the components and all of its children.
  682. """
  683. return self._cached_get_all_var_data
  684. def __post_init__(self):
  685. """Post-initialize the var."""
  686. pass
  687. class ArrayVar(ImmutableVar):
  688. """Base class for immutable array vars."""
  689. from reflex.experimental.vars.sequence import StringVar
  690. def join(self, sep: StringVar | str = "") -> ArrayJoinOperation:
  691. """Join the elements of the array.
  692. Args:
  693. sep: The separator between elements.
  694. Returns:
  695. The joined elements.
  696. """
  697. from reflex.experimental.vars.sequence import ArrayJoinOperation
  698. return ArrayJoinOperation(self, sep)
  699. @dataclasses.dataclass(
  700. eq=False,
  701. frozen=True,
  702. **{"slots": True} if sys.version_info >= (3, 10) else {},
  703. )
  704. class LiteralArrayVar(LiteralVar, ArrayVar):
  705. """Base class for immutable literal array vars."""
  706. _var_value: Union[
  707. List[Union[Var, Any]], Set[Union[Var, Any]], Tuple[Union[Var, Any], ...]
  708. ] = dataclasses.field(default_factory=list)
  709. def __init__(
  710. self,
  711. _var_value: list[Var | Any] | tuple[Var | Any] | set[Var | Any],
  712. _var_data: VarData | None = None,
  713. ):
  714. """Initialize the array var.
  715. Args:
  716. _var_value: The value of the var.
  717. _var_data: Additional hooks and imports associated with the Var.
  718. """
  719. super(LiteralArrayVar, self).__init__(
  720. _var_name="",
  721. _var_data=ImmutableVarData.merge(_var_data),
  722. _var_type=list,
  723. )
  724. object.__setattr__(self, "_var_value", _var_value)
  725. object.__delattr__(self, "_var_name")
  726. def __getattr__(self, name):
  727. """Get an attribute of the var.
  728. Args:
  729. name: The name of the attribute.
  730. Returns:
  731. The attribute of the var.
  732. """
  733. if name == "_var_name":
  734. return self._cached_var_name
  735. return super(type(self), self).__getattr__(name)
  736. @functools.cached_property
  737. def _cached_var_name(self) -> str:
  738. """The name of the var.
  739. Returns:
  740. The name of the var.
  741. """
  742. return (
  743. "["
  744. + ", ".join(
  745. [str(LiteralVar.create(element)) for element in self._var_value]
  746. )
  747. + "]"
  748. )
  749. @functools.cached_property
  750. def _cached_get_all_var_data(self) -> ImmutableVarData | None:
  751. """Get all VarData associated with the Var.
  752. Returns:
  753. The VarData of the components and all of its children.
  754. """
  755. return ImmutableVarData.merge(
  756. *[
  757. var._get_all_var_data()
  758. for var in self._var_value
  759. if isinstance(var, Var)
  760. ],
  761. self._var_data,
  762. )
  763. def _get_all_var_data(self) -> ImmutableVarData | None:
  764. """Wrapper method for cached property.
  765. Returns:
  766. The VarData of the components and all of its children.
  767. """
  768. return self._cached_get_all_var_data
  769. @dataclasses.dataclass(
  770. eq=False,
  771. frozen=True,
  772. **{"slots": True} if sys.version_info >= (3, 10) else {},
  773. )
  774. class StringSplitOperation(ArrayVar):
  775. """Base class for immutable array vars that are the result of a string split operation."""
  776. a: StringVar = dataclasses.field(
  777. default_factory=lambda: LiteralStringVar.create("")
  778. )
  779. b: StringVar = dataclasses.field(
  780. default_factory=lambda: LiteralStringVar.create("")
  781. )
  782. def __init__(
  783. self, a: StringVar | str, b: StringVar | str, _var_data: VarData | None = None
  784. ):
  785. """Initialize the string split operation var.
  786. Args:
  787. a: The string.
  788. b: The separator.
  789. _var_data: Additional hooks and imports associated with the Var.
  790. """
  791. super(StringSplitOperation, self).__init__(
  792. _var_name="",
  793. _var_type=list,
  794. _var_data=ImmutableVarData.merge(_var_data),
  795. )
  796. object.__setattr__(
  797. self, "a", a if isinstance(a, Var) else LiteralStringVar.create(a)
  798. )
  799. object.__setattr__(
  800. self, "b", b if isinstance(b, Var) else LiteralStringVar.create(b)
  801. )
  802. object.__delattr__(self, "_var_name")
  803. @cached_property
  804. def _cached_var_name(self) -> str:
  805. """The name of the var.
  806. Returns:
  807. The name of the var.
  808. """
  809. return f"{str(self.a)}.split({str(self.b)})"
  810. def __getattr__(self, name: str) -> Any:
  811. """Get an attribute of the var.
  812. Args:
  813. name: The name of the attribute.
  814. Returns:
  815. The attribute value.
  816. """
  817. if name == "_var_name":
  818. return self._cached_var_name
  819. getattr(super(StringSplitOperation, self), name)
  820. @cached_property
  821. def _cached_get_all_var_data(self) -> ImmutableVarData | None:
  822. """Get all VarData associated with the Var.
  823. Returns:
  824. The VarData of the components and all of its children.
  825. """
  826. return ImmutableVarData.merge(
  827. self.a._get_all_var_data(), self.b._get_all_var_data(), self._var_data
  828. )
  829. def _get_all_var_data(self) -> ImmutableVarData | None:
  830. return self._cached_get_all_var_data