sequence.py 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582
  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, int], ANOTHER_ARRAY_VAR]
  591. | ReflexCallable[[INNER_ARRAY_VAR], ANOTHER_ARRAY_VAR]
  592. | ReflexCallable[[], ANOTHER_ARRAY_VAR]
  593. ],
  594. ) -> CustomVarOperationReturn[Sequence[ANOTHER_ARRAY_VAR]]:
  595. """Map a function over an array.
  596. Args:
  597. array: The array.
  598. function: The function to map.
  599. Returns:
  600. The mapped array.
  601. """
  602. def type_computer(*args: Var):
  603. if not args:
  604. return (
  605. ReflexCallable[[List[Any], ReflexCallable], List[Any]],
  606. type_computer,
  607. )
  608. if len(args) == 1:
  609. return (
  610. ReflexCallable[[ReflexCallable], List[Any]],
  611. functools.partial(type_computer, *args),
  612. )
  613. return (ReflexCallable[[], List[args[0]._var_type]], None)
  614. return var_operation_return(
  615. js_expression=f"Array.prototype.map.apply({array}, [{function}])",
  616. type_computer=nary_type_computer(
  617. ReflexCallable[[List[Any], ReflexCallable], List[Any]],
  618. ReflexCallable[[ReflexCallable], List[Any]],
  619. computer=lambda args: List[unwrap_reflex_callalbe(args[1]._var_type)[1]], # type: ignore
  620. ),
  621. )
  622. @var_operation
  623. def array_concat_operation(
  624. lhs: Var[Sequence[INNER_ARRAY_VAR]], rhs: Var[Sequence[ANOTHER_ARRAY_VAR]]
  625. ) -> CustomVarOperationReturn[Sequence[INNER_ARRAY_VAR | ANOTHER_ARRAY_VAR]]:
  626. """Concatenate two arrays.
  627. Args:
  628. lhs: The left-hand side array.
  629. rhs: The right-hand side array.
  630. Returns:
  631. The concatenated array.
  632. """
  633. return var_operation_return(
  634. js_expression=f"[...{lhs}, ...{rhs}]",
  635. type_computer=nary_type_computer(
  636. ReflexCallable[[List[Any], List[Any]], List[Any]],
  637. ReflexCallable[[List[Any]], List[Any]],
  638. computer=lambda args: unionize(args[0]._var_type, args[1]._var_type),
  639. ),
  640. )
  641. @var_operation
  642. def string_concat_operation(
  643. lhs: Var[str], rhs: Var[str]
  644. ) -> CustomVarOperationReturn[str]:
  645. """Concatenate two strings.
  646. Args:
  647. lhs: The left-hand side string.
  648. rhs: The right-hand side string.
  649. Returns:
  650. The concatenated string.
  651. """
  652. return var_operation_return(
  653. js_expression=f"{lhs} + {rhs}",
  654. var_type=str,
  655. )
  656. @var_operation
  657. def reverse_string_concat_operation(
  658. lhs: Var[str], rhs: Var[str]
  659. ) -> CustomVarOperationReturn[str]:
  660. """Concatenate two strings in reverse order.
  661. Args:
  662. lhs: The left-hand side string.
  663. rhs: The right-hand side string.
  664. Returns:
  665. The concatenated string.
  666. """
  667. return var_operation_return(
  668. js_expression=f"{rhs} + {lhs}",
  669. var_type=str,
  670. )
  671. class SliceVar(Var[slice], python_types=slice):
  672. """Base class for immutable slice vars."""
  673. @dataclasses.dataclass(
  674. eq=False,
  675. frozen=True,
  676. **{"slots": True} if sys.version_info >= (3, 10) else {},
  677. )
  678. class LiteralSliceVar(CachedVarOperation, LiteralVar, SliceVar):
  679. """Base class for immutable literal slice vars."""
  680. _var_value: slice = dataclasses.field(default_factory=lambda: slice(None))
  681. @cached_property_no_lock
  682. def _cached_var_name(self) -> str:
  683. """The name of the var.
  684. Returns:
  685. The name of the var.
  686. """
  687. return f"[{LiteralVar.create(self._var_value.start)!s}, {LiteralVar.create(self._var_value.stop)!s}, {LiteralVar.create(self._var_value.step)!s}]"
  688. @cached_property_no_lock
  689. def _cached_get_all_var_data(self) -> VarData | None:
  690. """Get all the VarData asVarDatae Var.
  691. Returns:
  692. The VarData associated with the Var.
  693. """
  694. return VarData.merge(
  695. *[
  696. var._get_all_var_data()
  697. for var in [
  698. self._var_value.start,
  699. self._var_value.stop,
  700. self._var_value.step,
  701. ]
  702. if isinstance(var, Var)
  703. ],
  704. self._var_data,
  705. )
  706. @classmethod
  707. def create(
  708. cls,
  709. value: slice,
  710. _var_type: Type[slice] | None = None,
  711. _var_data: VarData | None = None,
  712. ) -> SliceVar:
  713. """Create a var from a slice value.
  714. Args:
  715. value: The value to create the var from.
  716. _var_type: The type of the var.
  717. _var_data: Additional hooks and imports associated with the Var.
  718. Returns:
  719. The var.
  720. """
  721. return cls(
  722. _js_expr="",
  723. _var_type=slice if _var_type is None else _var_type,
  724. _var_data=_var_data,
  725. _var_value=value,
  726. )
  727. def __hash__(self) -> int:
  728. """Get the hash of the var.
  729. Returns:
  730. The hash of the var.
  731. """
  732. return hash(
  733. (
  734. self.__class__.__name__,
  735. self._var_value.start,
  736. self._var_value.stop,
  737. self._var_value.step,
  738. )
  739. )
  740. def json(self) -> str:
  741. """Get the JSON representation of the var.
  742. Returns:
  743. The JSON representation of the var.
  744. """
  745. return json.dumps(
  746. [self._var_value.start, self._var_value.stop, self._var_value.step]
  747. )
  748. class ArrayVar(Var[ARRAY_VAR_TYPE], python_types=(Sequence, set)):
  749. """Base class for immutable array vars."""
  750. join = array_join_operation
  751. reverse = array_reverse_operation
  752. __add__ = array_concat_operation
  753. __getitem__ = array_item_or_slice_operation
  754. at = array_item_operation
  755. slice = array_slice_operation
  756. length = array_length_operation
  757. range: ClassVar[
  758. FunctionVar[
  759. ReflexCallable[
  760. [int, VarWithDefault[int | None], VarWithDefault[int]], Sequence[int]
  761. ]
  762. ]
  763. ] = array_range_operation
  764. contains = array_contains_field_operation
  765. pluck = array_pluck_operation
  766. __rmul__ = __mul__ = repeat_array_operation
  767. __lt__ = array_lt_operation
  768. __gt__ = array_gt_operation
  769. __le__ = array_le_operation
  770. __ge__ = array_ge_operation
  771. def foreach(
  772. self: ArrayVar[Sequence[INNER_ARRAY_VAR]],
  773. fn: Callable[[Var[INNER_ARRAY_VAR], NumberVar[int]], ANOTHER_ARRAY_VAR]
  774. | Callable[[Var[INNER_ARRAY_VAR]], ANOTHER_ARRAY_VAR]
  775. | Callable[[], ANOTHER_ARRAY_VAR],
  776. ) -> ArrayVar[Sequence[ANOTHER_ARRAY_VAR]]:
  777. """Apply a function to each element of the array.
  778. Args:
  779. fn: The function to apply.
  780. Returns:
  781. The array after applying the function.
  782. Raises:
  783. VarTypeError: If the function takes more than one argument.
  784. TypeError: If the function is a ComponentState.
  785. """
  786. from reflex.state import ComponentState
  787. from .function import ArgsFunctionOperation
  788. if not callable(fn):
  789. raise_unsupported_operand_types("foreach", (type(self), type(fn)))
  790. # get the number of arguments of the function
  791. required_num_args = len(
  792. [
  793. p
  794. for p in inspect.signature(fn).parameters.values()
  795. if p.default == p.empty
  796. ]
  797. )
  798. if required_num_args > 2:
  799. raise VarTypeError(
  800. "The function passed to foreach should take at most two arguments."
  801. )
  802. num_args = len(inspect.signature(fn).parameters)
  803. if (
  804. hasattr(fn, "__qualname__")
  805. and fn.__qualname__ == ComponentState.create.__qualname__
  806. ):
  807. raise TypeError(
  808. "Using a ComponentState as `render_fn` inside `rx.foreach` is not supported yet."
  809. )
  810. if num_args == 0:
  811. fn_result = fn() # pyright: ignore [reportCallIssue]
  812. return_value = Var.create(fn_result)
  813. simple_function_var: FunctionVar[ReflexCallable[[], ANOTHER_ARRAY_VAR]] = (
  814. ArgsFunctionOperation.create(
  815. (),
  816. return_value,
  817. _var_type=ReflexCallable[[], return_value._var_type],
  818. )
  819. )
  820. return map_array_operation(self, simple_function_var).guess_type()
  821. # generic number var
  822. number_var = Var("").to(NumberVar, int)
  823. first_arg_type = self.__getitem__(number_var)._var_type
  824. arg_name = get_unique_variable_name()
  825. # get first argument type
  826. first_arg = cast(
  827. Var[Any],
  828. Var(
  829. _js_expr=arg_name,
  830. _var_type=first_arg_type,
  831. ).guess_type(),
  832. )
  833. if required_num_args < 2:
  834. fn_result = fn(first_arg) # pyright: ignore [reportCallIssue]
  835. return_value = Var.create(fn_result)
  836. function_var = cast(
  837. Var[ReflexCallable[[INNER_ARRAY_VAR], ANOTHER_ARRAY_VAR]],
  838. ArgsFunctionOperation.create(
  839. (arg_name,),
  840. return_value,
  841. _var_type=ReflexCallable[[first_arg_type], return_value._var_type],
  842. ),
  843. )
  844. return map_array_operation.call(self, function_var).guess_type()
  845. second_arg = cast(
  846. NumberVar[int],
  847. Var(
  848. _js_expr=get_unique_variable_name(),
  849. _var_type=int,
  850. ).guess_type(),
  851. )
  852. fn_result = fn(first_arg, second_arg) # pyright: ignore [reportCallIssue]
  853. return_value = Var.create(fn_result)
  854. function_var = cast(
  855. Var[ReflexCallable[[INNER_ARRAY_VAR, int], ANOTHER_ARRAY_VAR]],
  856. ArgsFunctionOperation.create(
  857. (arg_name, second_arg._js_expr),
  858. return_value,
  859. _var_type=ReflexCallable[[first_arg_type, int], return_value._var_type],
  860. ),
  861. )
  862. return map_array_operation.call(self, function_var).guess_type()
  863. LIST_ELEMENT = TypeVar("LIST_ELEMENT", covariant=True)
  864. ARRAY_VAR_OF_LIST_ELEMENT = TypeAliasType(
  865. "ARRAY_VAR_OF_LIST_ELEMENT",
  866. Union[
  867. ArrayVar[Sequence[LIST_ELEMENT]],
  868. ArrayVar[Set[LIST_ELEMENT]],
  869. ],
  870. type_params=(LIST_ELEMENT,),
  871. )
  872. @dataclasses.dataclass(
  873. eq=False,
  874. frozen=True,
  875. **{"slots": True} if sys.version_info >= (3, 10) else {},
  876. )
  877. class LiteralArrayVar(CachedVarOperation, LiteralVar, ArrayVar[ARRAY_VAR_TYPE]):
  878. """Base class for immutable literal array vars."""
  879. _var_value: Union[
  880. Sequence[Union[Var, Any]],
  881. Set[Union[Var, Any]],
  882. ] = dataclasses.field(default_factory=list)
  883. @cached_property_no_lock
  884. def _cached_var_name(self) -> str:
  885. """The name of the var.
  886. Returns:
  887. The name of the var.
  888. """
  889. return (
  890. "["
  891. + ", ".join(
  892. [str(LiteralVar.create(element)) for element in self._var_value]
  893. )
  894. + "]"
  895. )
  896. @cached_property_no_lock
  897. def _cached_get_all_var_data(self) -> VarData | None:
  898. """Get all the VarData associated with the Var.
  899. Returns:
  900. The VarData associated with the Var.
  901. """
  902. return VarData.merge(
  903. *[
  904. LiteralVar.create(element)._get_all_var_data()
  905. for element in self._var_value
  906. ],
  907. self._var_data,
  908. )
  909. def __hash__(self) -> int:
  910. """Get the hash of the var.
  911. Returns:
  912. The hash of the var.
  913. """
  914. return hash((self.__class__.__name__, self._js_expr))
  915. def json(self) -> str:
  916. """Get the JSON representation of the var.
  917. Returns:
  918. The JSON representation of the var.
  919. """
  920. return (
  921. "["
  922. + ", ".join(
  923. [LiteralVar.create(element).json() for element in self._var_value]
  924. )
  925. + "]"
  926. )
  927. @classmethod
  928. def create(
  929. cls,
  930. value: ARRAY_VAR_TYPE,
  931. _var_type: Type[ARRAY_VAR_TYPE] | None = None,
  932. _var_data: VarData | None = None,
  933. ) -> LiteralArrayVar[ARRAY_VAR_TYPE]:
  934. """Create a var from a string value.
  935. Args:
  936. value: The value to create the var from.
  937. _var_data: Additional hooks and imports associated with the Var.
  938. Returns:
  939. The var.
  940. """
  941. return cls(
  942. _js_expr="",
  943. _var_type=figure_out_type(value) if _var_type is None else _var_type,
  944. _var_data=_var_data,
  945. _var_value=value,
  946. )
  947. class StringVar(Var[STRING_TYPE], python_types=str):
  948. """Base class for immutable string vars."""
  949. __add__ = string_concat_operation
  950. __radd__ = reverse_string_concat_operation
  951. __getitem__ = string_index_or_slice_operation
  952. at = string_item_operation
  953. slice = string_slice_operation
  954. lower = string_lower_operation
  955. upper = string_upper_operation
  956. strip = string_strip_operation
  957. contains = string_contains_field_operation
  958. split = string_split_operation
  959. length = split.chain(array_length_operation)
  960. reversed = split.chain(array_reverse_operation).chain(array_join_operation)
  961. startswith = string_starts_with_operation
  962. __rmul__ = __mul__ = repeat_string_operation
  963. __lt__ = string_lt_operation
  964. __gt__ = string_gt_operation
  965. __le__ = string_le_operation
  966. __ge__ = string_ge_operation
  967. # Compile regex for finding reflex var tags.
  968. _decode_var_pattern_re = (
  969. rf"{constants.REFLEX_VAR_OPENING_TAG}(.*?){constants.REFLEX_VAR_CLOSING_TAG}"
  970. )
  971. _decode_var_pattern = re.compile(_decode_var_pattern_re, flags=re.DOTALL)
  972. @dataclasses.dataclass(
  973. eq=False,
  974. frozen=True,
  975. **{"slots": True} if sys.version_info >= (3, 10) else {},
  976. )
  977. class LiteralStringVar(LiteralVar, StringVar[str]):
  978. """Base class for immutable literal string vars."""
  979. _var_value: str = dataclasses.field(default="")
  980. @classmethod
  981. def create(
  982. cls,
  983. value: str,
  984. _var_type: GenericType | None = None,
  985. _var_data: VarData | None = None,
  986. ) -> StringVar:
  987. """Create a var from a string value.
  988. Args:
  989. value: The value to create the var from.
  990. _var_type: The type of the var.
  991. _var_data: Additional hooks and imports associated with the Var.
  992. Returns:
  993. The var.
  994. """
  995. # Determine var type in case the value is inherited from str.
  996. _var_type = _var_type or type(value) or str
  997. if REFLEX_VAR_OPENING_TAG in value:
  998. strings_and_vals: list[Var | str] = []
  999. offset = 0
  1000. # Find all tags
  1001. while m := _decode_var_pattern.search(value):
  1002. start, end = m.span()
  1003. strings_and_vals.append(value[:start])
  1004. serialized_data = m.group(1)
  1005. if serialized_data.isnumeric() or (
  1006. serialized_data[0] == "-" and serialized_data[1:].isnumeric()
  1007. ):
  1008. # This is a global immutable var.
  1009. var = _global_vars[int(serialized_data)]
  1010. strings_and_vals.append(var)
  1011. value = value[(end + len(var._js_expr)) :]
  1012. offset += end - start
  1013. strings_and_vals.append(value)
  1014. filtered_strings_and_vals = [
  1015. s for s in strings_and_vals if isinstance(s, Var) or s
  1016. ]
  1017. if len(filtered_strings_and_vals) == 1:
  1018. only_string = filtered_strings_and_vals[0]
  1019. if isinstance(only_string, str):
  1020. return LiteralVar.create(only_string).to(StringVar, _var_type)
  1021. else:
  1022. return only_string.to(StringVar, only_string._var_type)
  1023. if len(
  1024. literal_strings := [
  1025. s
  1026. for s in filtered_strings_and_vals
  1027. if isinstance(s, (str, LiteralStringVar))
  1028. ]
  1029. ) == len(filtered_strings_and_vals):
  1030. return LiteralStringVar.create(
  1031. "".join(
  1032. s._var_value if isinstance(s, LiteralStringVar) else s
  1033. for s in literal_strings
  1034. ),
  1035. _var_type=_var_type,
  1036. _var_data=VarData.merge(
  1037. _var_data,
  1038. *(
  1039. s._get_all_var_data()
  1040. for s in filtered_strings_and_vals
  1041. if isinstance(s, Var)
  1042. ),
  1043. ),
  1044. )
  1045. concat_result = ConcatVarOperation.create(
  1046. *filtered_strings_and_vals,
  1047. _var_data=_var_data,
  1048. )
  1049. return (
  1050. concat_result
  1051. if _var_type is str
  1052. else concat_result.to(StringVar, _var_type)
  1053. )
  1054. return LiteralStringVar(
  1055. _js_expr=json.dumps(value),
  1056. _var_type=_var_type,
  1057. _var_data=_var_data,
  1058. _var_value=value,
  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._var_value))
  1066. def json(self) -> str:
  1067. """Get the JSON representation of the var.
  1068. Returns:
  1069. The JSON representation of the var.
  1070. """
  1071. return json.dumps(self._var_value)
  1072. @dataclasses.dataclass(
  1073. eq=False,
  1074. frozen=True,
  1075. **{"slots": True} if sys.version_info >= (3, 10) else {},
  1076. )
  1077. class ConcatVarOperation(CachedVarOperation, StringVar[str]):
  1078. """Representing a concatenation of literal string vars."""
  1079. _var_value: Tuple[Var, ...] = dataclasses.field(default_factory=tuple)
  1080. @cached_property_no_lock
  1081. def _cached_var_name(self) -> str:
  1082. """The name of the var.
  1083. Returns:
  1084. The name of the var.
  1085. """
  1086. list_of_strs: List[Union[str, Var]] = []
  1087. last_string = ""
  1088. for var in self._var_value:
  1089. if isinstance(var, LiteralStringVar):
  1090. last_string += var._var_value
  1091. else:
  1092. if last_string:
  1093. list_of_strs.append(last_string)
  1094. last_string = ""
  1095. list_of_strs.append(var)
  1096. if last_string:
  1097. list_of_strs.append(last_string)
  1098. list_of_strs_filtered = [
  1099. str(LiteralVar.create(s)) for s in list_of_strs if isinstance(s, Var) or s
  1100. ]
  1101. if len(list_of_strs_filtered) == 1:
  1102. return list_of_strs_filtered[0]
  1103. return "(" + "+".join(list_of_strs_filtered) + ")"
  1104. @cached_property_no_lock
  1105. def _cached_get_all_var_data(self) -> VarData | None:
  1106. """Get all the VarData asVarDatae Var.
  1107. Returns:
  1108. The VarData associated with the Var.
  1109. """
  1110. return VarData.merge(
  1111. *[
  1112. var._get_all_var_data()
  1113. for var in self._var_value
  1114. if isinstance(var, Var)
  1115. ],
  1116. self._var_data,
  1117. )
  1118. @classmethod
  1119. def create(
  1120. cls,
  1121. *value: Var | str,
  1122. _var_data: VarData | None = None,
  1123. ) -> ConcatVarOperation:
  1124. """Create a var from a string value.
  1125. Args:
  1126. value: The values to concatenate.
  1127. _var_data: Additional hooks and imports associated with the Var.
  1128. Returns:
  1129. The var.
  1130. """
  1131. return cls(
  1132. _js_expr="",
  1133. _var_type=str,
  1134. _var_data=_var_data,
  1135. _var_value=tuple(map(LiteralVar.create, value)),
  1136. )
  1137. def is_tuple_type(t: GenericType) -> bool:
  1138. """Check if a type is a tuple type.
  1139. Args:
  1140. t: The type to check.
  1141. Returns:
  1142. Whether the type is a tuple type.
  1143. """
  1144. if inspect.isclass(t):
  1145. return issubclass(t, tuple)
  1146. return get_origin(t) is tuple
  1147. class ColorVar(StringVar[Color], python_types=Color):
  1148. """Base class for immutable color vars."""
  1149. @dataclasses.dataclass(
  1150. eq=False,
  1151. frozen=True,
  1152. **{"slots": True} if sys.version_info >= (3, 10) else {},
  1153. )
  1154. class LiteralColorVar(CachedVarOperation, LiteralVar, ColorVar):
  1155. """Base class for immutable literal color vars."""
  1156. _var_value: Color = dataclasses.field(default_factory=lambda: Color(color="black"))
  1157. @classmethod
  1158. def create(
  1159. cls,
  1160. value: Color,
  1161. _var_type: Type[Color] | None = None,
  1162. _var_data: VarData | None = None,
  1163. ) -> ColorVar:
  1164. """Create a var from a string value.
  1165. Args:
  1166. value: The value to create the var from.
  1167. _var_type: The type of the var.
  1168. _var_data: Additional hooks and imports associated with the Var.
  1169. Returns:
  1170. The var.
  1171. """
  1172. return cls(
  1173. _js_expr="",
  1174. _var_type=_var_type or Color,
  1175. _var_data=_var_data,
  1176. _var_value=value,
  1177. )
  1178. def __hash__(self) -> int:
  1179. """Get the hash of the var.
  1180. Returns:
  1181. The hash of the var.
  1182. """
  1183. return hash(
  1184. (
  1185. self.__class__.__name__,
  1186. self._var_value.color,
  1187. self._var_value.alpha,
  1188. self._var_value.shade,
  1189. )
  1190. )
  1191. @cached_property_no_lock
  1192. def _cached_var_name(self) -> str:
  1193. """The name of the var.
  1194. Returns:
  1195. The name of the var.
  1196. """
  1197. alpha = cast(Union[Var[bool], bool], self._var_value.alpha)
  1198. alpha = (
  1199. ternary_operation(
  1200. alpha,
  1201. LiteralStringVar.create("a"),
  1202. LiteralStringVar.create(""),
  1203. )
  1204. if isinstance(alpha, Var)
  1205. else LiteralStringVar.create("a" if alpha else "")
  1206. )
  1207. shade = self._var_value.shade
  1208. shade = (
  1209. shade.to_string(use_json=False)
  1210. if isinstance(shade, Var)
  1211. else LiteralStringVar.create(str(shade))
  1212. )
  1213. return str(
  1214. ConcatVarOperation.create(
  1215. LiteralStringVar.create("var(--"),
  1216. self._var_value.color,
  1217. LiteralStringVar.create("-"),
  1218. alpha,
  1219. shade,
  1220. LiteralStringVar.create(")"),
  1221. )
  1222. )
  1223. @cached_property_no_lock
  1224. def _cached_get_all_var_data(self) -> VarData | None:
  1225. """Get all the var data.
  1226. Returns:
  1227. The var data.
  1228. """
  1229. return VarData.merge(
  1230. *[
  1231. LiteralVar.create(var)._get_all_var_data()
  1232. for var in (
  1233. self._var_value.color,
  1234. self._var_value.alpha,
  1235. self._var_value.shade,
  1236. )
  1237. ],
  1238. self._var_data,
  1239. )
  1240. def json(self) -> str:
  1241. """Get the JSON representation of the var.
  1242. Returns:
  1243. The JSON representation of the var.
  1244. Raises:
  1245. TypeError: If the color is not a valid color.
  1246. """
  1247. color, alpha, shade = map(
  1248. get_python_literal,
  1249. (self._var_value.color, self._var_value.alpha, self._var_value.shade),
  1250. )
  1251. if color is None or alpha is None or shade is None:
  1252. raise TypeError("Cannot serialize color that contains non-literal vars.")
  1253. if (
  1254. not isinstance(color, str)
  1255. or not isinstance(alpha, bool)
  1256. or not isinstance(shade, int)
  1257. ):
  1258. raise TypeError("Color is not a valid color.")
  1259. return f"var(--{color}-{'a' if alpha else ''}{shade})"