sequence.py 40 KB

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