sequence.py 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573
  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. num_args = len(inspect.signature(fn).parameters)
  792. if num_args > 2:
  793. raise VarTypeError(
  794. "The function passed to foreach should take at most two arguments."
  795. )
  796. if (
  797. hasattr(fn, "__qualname__")
  798. and fn.__qualname__ == ComponentState.create.__qualname__
  799. ):
  800. raise TypeError(
  801. "Using a ComponentState as `render_fn` inside `rx.foreach` is not supported yet."
  802. )
  803. if num_args == 0:
  804. fn_result = fn() # pyright: ignore [reportCallIssue]
  805. return_value = Var.create(fn_result)
  806. simple_function_var: FunctionVar[ReflexCallable[[], ANOTHER_ARRAY_VAR]] = (
  807. ArgsFunctionOperation.create(
  808. (),
  809. return_value,
  810. _var_type=ReflexCallable[[], return_value._var_type],
  811. )
  812. )
  813. return map_array_operation(self, simple_function_var).guess_type()
  814. # generic number var
  815. number_var = Var("").to(NumberVar, int)
  816. first_arg_type = self.__getitem__(number_var)._var_type
  817. arg_name = get_unique_variable_name()
  818. # get first argument type
  819. first_arg = cast(
  820. Var[Any],
  821. Var(
  822. _js_expr=arg_name,
  823. _var_type=first_arg_type,
  824. ).guess_type(),
  825. )
  826. if num_args == 1:
  827. fn_result = fn(first_arg) # pyright: ignore [reportCallIssue]
  828. return_value = Var.create(fn_result)
  829. function_var = cast(
  830. Var[ReflexCallable[[INNER_ARRAY_VAR], ANOTHER_ARRAY_VAR]],
  831. ArgsFunctionOperation.create(
  832. (arg_name,),
  833. return_value,
  834. _var_type=ReflexCallable[[first_arg_type], return_value._var_type],
  835. ),
  836. )
  837. return map_array_operation.call(self, function_var).guess_type()
  838. second_arg = cast(
  839. NumberVar[int],
  840. Var(
  841. _js_expr=get_unique_variable_name(),
  842. _var_type=int,
  843. ).guess_type(),
  844. )
  845. fn_result = fn(first_arg, second_arg) # pyright: ignore [reportCallIssue]
  846. return_value = Var.create(fn_result)
  847. function_var = cast(
  848. Var[ReflexCallable[[INNER_ARRAY_VAR, int], ANOTHER_ARRAY_VAR]],
  849. ArgsFunctionOperation.create(
  850. (arg_name, second_arg._js_expr),
  851. return_value,
  852. _var_type=ReflexCallable[[first_arg_type, int], return_value._var_type],
  853. ),
  854. )
  855. return map_array_operation.call(self, function_var).guess_type()
  856. LIST_ELEMENT = TypeVar("LIST_ELEMENT", covariant=True)
  857. ARRAY_VAR_OF_LIST_ELEMENT = TypeAliasType(
  858. "ARRAY_VAR_OF_LIST_ELEMENT",
  859. Union[
  860. ArrayVar[Sequence[LIST_ELEMENT]],
  861. ArrayVar[Set[LIST_ELEMENT]],
  862. ],
  863. type_params=(LIST_ELEMENT,),
  864. )
  865. @dataclasses.dataclass(
  866. eq=False,
  867. frozen=True,
  868. **{"slots": True} if sys.version_info >= (3, 10) else {},
  869. )
  870. class LiteralArrayVar(CachedVarOperation, LiteralVar, ArrayVar[ARRAY_VAR_TYPE]):
  871. """Base class for immutable literal array vars."""
  872. _var_value: Union[
  873. Sequence[Union[Var, Any]],
  874. Set[Union[Var, Any]],
  875. ] = dataclasses.field(default_factory=list)
  876. @cached_property_no_lock
  877. def _cached_var_name(self) -> str:
  878. """The name of the var.
  879. Returns:
  880. The name of the var.
  881. """
  882. return (
  883. "["
  884. + ", ".join(
  885. [str(LiteralVar.create(element)) for element in self._var_value]
  886. )
  887. + "]"
  888. )
  889. @cached_property_no_lock
  890. def _cached_get_all_var_data(self) -> VarData | None:
  891. """Get all the VarData associated with the Var.
  892. Returns:
  893. The VarData associated with the Var.
  894. """
  895. return VarData.merge(
  896. *[
  897. LiteralVar.create(element)._get_all_var_data()
  898. for element in self._var_value
  899. ],
  900. self._var_data,
  901. )
  902. def __hash__(self) -> int:
  903. """Get the hash of the var.
  904. Returns:
  905. The hash of the var.
  906. """
  907. return hash((self.__class__.__name__, self._js_expr))
  908. def json(self) -> str:
  909. """Get the JSON representation of the var.
  910. Returns:
  911. The JSON representation of the var.
  912. """
  913. return (
  914. "["
  915. + ", ".join(
  916. [LiteralVar.create(element).json() for element in self._var_value]
  917. )
  918. + "]"
  919. )
  920. @classmethod
  921. def create(
  922. cls,
  923. value: ARRAY_VAR_TYPE,
  924. _var_type: Type[ARRAY_VAR_TYPE] | None = None,
  925. _var_data: VarData | None = None,
  926. ) -> LiteralArrayVar[ARRAY_VAR_TYPE]:
  927. """Create a var from a string value.
  928. Args:
  929. value: The value to create the var from.
  930. _var_data: Additional hooks and imports associated with the Var.
  931. Returns:
  932. The var.
  933. """
  934. return cls(
  935. _js_expr="",
  936. _var_type=figure_out_type(value) if _var_type is None else _var_type,
  937. _var_data=_var_data,
  938. _var_value=value,
  939. )
  940. class StringVar(Var[STRING_TYPE], python_types=str):
  941. """Base class for immutable string vars."""
  942. __add__ = string_concat_operation
  943. __radd__ = reverse_string_concat_operation
  944. __getitem__ = string_index_or_slice_operation
  945. at = string_item_operation
  946. slice = string_slice_operation
  947. lower = string_lower_operation
  948. upper = string_upper_operation
  949. strip = string_strip_operation
  950. contains = string_contains_field_operation
  951. split = string_split_operation
  952. length = split.chain(array_length_operation)
  953. reversed = split.chain(array_reverse_operation).chain(array_join_operation)
  954. startswith = string_starts_with_operation
  955. __rmul__ = __mul__ = repeat_string_operation
  956. __lt__ = string_lt_operation
  957. __gt__ = string_gt_operation
  958. __le__ = string_le_operation
  959. __ge__ = string_ge_operation
  960. # Compile regex for finding reflex var tags.
  961. _decode_var_pattern_re = (
  962. rf"{constants.REFLEX_VAR_OPENING_TAG}(.*?){constants.REFLEX_VAR_CLOSING_TAG}"
  963. )
  964. _decode_var_pattern = re.compile(_decode_var_pattern_re, flags=re.DOTALL)
  965. @dataclasses.dataclass(
  966. eq=False,
  967. frozen=True,
  968. **{"slots": True} if sys.version_info >= (3, 10) else {},
  969. )
  970. class LiteralStringVar(LiteralVar, StringVar[str]):
  971. """Base class for immutable literal string vars."""
  972. _var_value: str = dataclasses.field(default="")
  973. @classmethod
  974. def create(
  975. cls,
  976. value: str,
  977. _var_type: GenericType | None = None,
  978. _var_data: VarData | None = None,
  979. ) -> StringVar:
  980. """Create a var from a string value.
  981. Args:
  982. value: The value to create the var from.
  983. _var_type: The type of the var.
  984. _var_data: Additional hooks and imports associated with the Var.
  985. Returns:
  986. The var.
  987. """
  988. # Determine var type in case the value is inherited from str.
  989. _var_type = _var_type or type(value) or str
  990. if REFLEX_VAR_OPENING_TAG in value:
  991. strings_and_vals: list[Var | str] = []
  992. offset = 0
  993. # Find all tags
  994. while m := _decode_var_pattern.search(value):
  995. start, end = m.span()
  996. strings_and_vals.append(value[:start])
  997. serialized_data = m.group(1)
  998. if serialized_data.isnumeric() or (
  999. serialized_data[0] == "-" and serialized_data[1:].isnumeric()
  1000. ):
  1001. # This is a global immutable var.
  1002. var = _global_vars[int(serialized_data)]
  1003. strings_and_vals.append(var)
  1004. value = value[(end + len(var._js_expr)) :]
  1005. offset += end - start
  1006. strings_and_vals.append(value)
  1007. filtered_strings_and_vals = [
  1008. s for s in strings_and_vals if isinstance(s, Var) or s
  1009. ]
  1010. if len(filtered_strings_and_vals) == 1:
  1011. only_string = filtered_strings_and_vals[0]
  1012. if isinstance(only_string, str):
  1013. return LiteralVar.create(only_string).to(StringVar, _var_type)
  1014. else:
  1015. return only_string.to(StringVar, only_string._var_type)
  1016. if len(
  1017. literal_strings := [
  1018. s
  1019. for s in filtered_strings_and_vals
  1020. if isinstance(s, (str, LiteralStringVar))
  1021. ]
  1022. ) == len(filtered_strings_and_vals):
  1023. return LiteralStringVar.create(
  1024. "".join(
  1025. s._var_value if isinstance(s, LiteralStringVar) else s
  1026. for s in literal_strings
  1027. ),
  1028. _var_type=_var_type,
  1029. _var_data=VarData.merge(
  1030. _var_data,
  1031. *(
  1032. s._get_all_var_data()
  1033. for s in filtered_strings_and_vals
  1034. if isinstance(s, Var)
  1035. ),
  1036. ),
  1037. )
  1038. concat_result = ConcatVarOperation.create(
  1039. *filtered_strings_and_vals,
  1040. _var_data=_var_data,
  1041. )
  1042. return (
  1043. concat_result
  1044. if _var_type is str
  1045. else concat_result.to(StringVar, _var_type)
  1046. )
  1047. return LiteralStringVar(
  1048. _js_expr=json.dumps(value),
  1049. _var_type=_var_type,
  1050. _var_data=_var_data,
  1051. _var_value=value,
  1052. )
  1053. def __hash__(self) -> int:
  1054. """Get the hash of the var.
  1055. Returns:
  1056. The hash of the var.
  1057. """
  1058. return hash((self.__class__.__name__, self._var_value))
  1059. def json(self) -> str:
  1060. """Get the JSON representation of the var.
  1061. Returns:
  1062. The JSON representation of the var.
  1063. """
  1064. return json.dumps(self._var_value)
  1065. @dataclasses.dataclass(
  1066. eq=False,
  1067. frozen=True,
  1068. **{"slots": True} if sys.version_info >= (3, 10) else {},
  1069. )
  1070. class ConcatVarOperation(CachedVarOperation, StringVar[str]):
  1071. """Representing a concatenation of literal string vars."""
  1072. _var_value: Tuple[Var, ...] = dataclasses.field(default_factory=tuple)
  1073. @cached_property_no_lock
  1074. def _cached_var_name(self) -> str:
  1075. """The name of the var.
  1076. Returns:
  1077. The name of the var.
  1078. """
  1079. list_of_strs: List[Union[str, Var]] = []
  1080. last_string = ""
  1081. for var in self._var_value:
  1082. if isinstance(var, LiteralStringVar):
  1083. last_string += var._var_value
  1084. else:
  1085. if last_string:
  1086. list_of_strs.append(last_string)
  1087. last_string = ""
  1088. list_of_strs.append(var)
  1089. if last_string:
  1090. list_of_strs.append(last_string)
  1091. list_of_strs_filtered = [
  1092. str(LiteralVar.create(s)) for s in list_of_strs if isinstance(s, Var) or s
  1093. ]
  1094. if len(list_of_strs_filtered) == 1:
  1095. return list_of_strs_filtered[0]
  1096. return "(" + "+".join(list_of_strs_filtered) + ")"
  1097. @cached_property_no_lock
  1098. def _cached_get_all_var_data(self) -> VarData | None:
  1099. """Get all the VarData asVarDatae Var.
  1100. Returns:
  1101. The VarData associated with the Var.
  1102. """
  1103. return VarData.merge(
  1104. *[
  1105. var._get_all_var_data()
  1106. for var in self._var_value
  1107. if isinstance(var, Var)
  1108. ],
  1109. self._var_data,
  1110. )
  1111. @classmethod
  1112. def create(
  1113. cls,
  1114. *value: Var | str,
  1115. _var_data: VarData | None = None,
  1116. ) -> ConcatVarOperation:
  1117. """Create a var from a string value.
  1118. Args:
  1119. value: The values to concatenate.
  1120. _var_data: Additional hooks and imports associated with the Var.
  1121. Returns:
  1122. The var.
  1123. """
  1124. return cls(
  1125. _js_expr="",
  1126. _var_type=str,
  1127. _var_data=_var_data,
  1128. _var_value=tuple(map(LiteralVar.create, value)),
  1129. )
  1130. def is_tuple_type(t: GenericType) -> bool:
  1131. """Check if a type is a tuple type.
  1132. Args:
  1133. t: The type to check.
  1134. Returns:
  1135. Whether the type is a tuple type.
  1136. """
  1137. if inspect.isclass(t):
  1138. return issubclass(t, tuple)
  1139. return get_origin(t) is tuple
  1140. class ColorVar(StringVar[Color], python_types=Color):
  1141. """Base class for immutable color vars."""
  1142. @dataclasses.dataclass(
  1143. eq=False,
  1144. frozen=True,
  1145. **{"slots": True} if sys.version_info >= (3, 10) else {},
  1146. )
  1147. class LiteralColorVar(CachedVarOperation, LiteralVar, ColorVar):
  1148. """Base class for immutable literal color vars."""
  1149. _var_value: Color = dataclasses.field(default_factory=lambda: Color(color="black"))
  1150. @classmethod
  1151. def create(
  1152. cls,
  1153. value: Color,
  1154. _var_type: Type[Color] | None = None,
  1155. _var_data: VarData | None = None,
  1156. ) -> ColorVar:
  1157. """Create a var from a string value.
  1158. Args:
  1159. value: The value to create the var from.
  1160. _var_type: The type of the var.
  1161. _var_data: Additional hooks and imports associated with the Var.
  1162. Returns:
  1163. The var.
  1164. """
  1165. return cls(
  1166. _js_expr="",
  1167. _var_type=_var_type or Color,
  1168. _var_data=_var_data,
  1169. _var_value=value,
  1170. )
  1171. def __hash__(self) -> int:
  1172. """Get the hash of the var.
  1173. Returns:
  1174. The hash of the var.
  1175. """
  1176. return hash(
  1177. (
  1178. self.__class__.__name__,
  1179. self._var_value.color,
  1180. self._var_value.alpha,
  1181. self._var_value.shade,
  1182. )
  1183. )
  1184. @cached_property_no_lock
  1185. def _cached_var_name(self) -> str:
  1186. """The name of the var.
  1187. Returns:
  1188. The name of the var.
  1189. """
  1190. alpha = cast(Union[Var[bool], bool], self._var_value.alpha)
  1191. alpha = (
  1192. ternary_operation(
  1193. alpha,
  1194. LiteralStringVar.create("a"),
  1195. LiteralStringVar.create(""),
  1196. )
  1197. if isinstance(alpha, Var)
  1198. else LiteralStringVar.create("a" if alpha else "")
  1199. )
  1200. shade = self._var_value.shade
  1201. shade = (
  1202. shade.to_string(use_json=False)
  1203. if isinstance(shade, Var)
  1204. else LiteralStringVar.create(str(shade))
  1205. )
  1206. return str(
  1207. ConcatVarOperation.create(
  1208. LiteralStringVar.create("var(--"),
  1209. self._var_value.color,
  1210. LiteralStringVar.create("-"),
  1211. alpha,
  1212. shade,
  1213. LiteralStringVar.create(")"),
  1214. )
  1215. )
  1216. @cached_property_no_lock
  1217. def _cached_get_all_var_data(self) -> VarData | None:
  1218. """Get all the var data.
  1219. Returns:
  1220. The var data.
  1221. """
  1222. return VarData.merge(
  1223. *[
  1224. LiteralVar.create(var)._get_all_var_data()
  1225. for var in (
  1226. self._var_value.color,
  1227. self._var_value.alpha,
  1228. self._var_value.shade,
  1229. )
  1230. ],
  1231. self._var_data,
  1232. )
  1233. def json(self) -> str:
  1234. """Get the JSON representation of the var.
  1235. Returns:
  1236. The JSON representation of the var.
  1237. Raises:
  1238. TypeError: If the color is not a valid color.
  1239. """
  1240. color, alpha, shade = map(
  1241. get_python_literal,
  1242. (self._var_value.color, self._var_value.alpha, self._var_value.shade),
  1243. )
  1244. if color is None or alpha is None or shade is None:
  1245. raise TypeError("Cannot serialize color that contains non-literal vars.")
  1246. if (
  1247. not isinstance(color, str)
  1248. or not isinstance(alpha, bool)
  1249. or not isinstance(shade, int)
  1250. ):
  1251. raise TypeError("Color is not a valid color.")
  1252. return f"var(--{color}-{'a' if alpha else ''}{shade})"