sequence.py 53 KB

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