number.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131
  1. """Immutable number vars."""
  2. from __future__ import annotations
  3. import dataclasses
  4. import json
  5. import math
  6. import sys
  7. from typing import (
  8. TYPE_CHECKING,
  9. Any,
  10. Callable,
  11. NoReturn,
  12. Type,
  13. TypeVar,
  14. Union,
  15. overload,
  16. )
  17. from reflex.constants.base import Dirs
  18. from reflex.utils.exceptions import PrimitiveUnserializableToJSONError, VarTypeError
  19. from reflex.utils.imports import ImportDict, ImportVar
  20. from .base import (
  21. CustomVarOperationReturn,
  22. LiteralVar,
  23. Var,
  24. VarData,
  25. unionize,
  26. var_operation,
  27. var_operation_return,
  28. )
  29. NUMBER_T = TypeVar("NUMBER_T", int, float, Union[int, float], bool)
  30. if TYPE_CHECKING:
  31. from .sequence import ArrayVar
  32. def raise_unsupported_operand_types(
  33. operator: str, operands_types: tuple[type, ...]
  34. ) -> NoReturn:
  35. """Raise an unsupported operand types error.
  36. Args:
  37. operator: The operator.
  38. operands_types: The types of the operands.
  39. Raises:
  40. VarTypeError: The operand types are unsupported.
  41. """
  42. raise VarTypeError(
  43. f"Unsupported Operand type(s) for {operator}: {', '.join(t.__name__ for t in operands_types)}"
  44. )
  45. class NumberVar(Var[NUMBER_T], python_types=(int, float)):
  46. """Base class for immutable number vars."""
  47. @overload
  48. def __add__(self, other: number_types) -> NumberVar: ...
  49. @overload
  50. def __add__(self, other: NoReturn) -> NoReturn: ...
  51. def __add__(self, other: Any):
  52. """Add two numbers.
  53. Args:
  54. other: The other number.
  55. Returns:
  56. The number addition operation.
  57. """
  58. if not isinstance(other, NUMBER_TYPES):
  59. raise_unsupported_operand_types("+", (type(self), type(other)))
  60. return number_add_operation(self, +other)
  61. @overload
  62. def __radd__(self, other: number_types) -> NumberVar: ...
  63. @overload
  64. def __radd__(self, other: NoReturn) -> NoReturn: ...
  65. def __radd__(self, other: Any):
  66. """Add two numbers.
  67. Args:
  68. other: The other number.
  69. Returns:
  70. The number addition operation.
  71. """
  72. if not isinstance(other, NUMBER_TYPES):
  73. raise_unsupported_operand_types("+", (type(other), type(self)))
  74. return number_add_operation(+other, self)
  75. @overload
  76. def __sub__(self, other: number_types) -> NumberVar: ...
  77. @overload
  78. def __sub__(self, other: NoReturn) -> NoReturn: ...
  79. def __sub__(self, other: Any):
  80. """Subtract two numbers.
  81. Args:
  82. other: The other number.
  83. Returns:
  84. The number subtraction operation.
  85. """
  86. if not isinstance(other, NUMBER_TYPES):
  87. raise_unsupported_operand_types("-", (type(self), type(other)))
  88. return number_subtract_operation(self, +other)
  89. @overload
  90. def __rsub__(self, other: number_types) -> NumberVar: ...
  91. @overload
  92. def __rsub__(self, other: NoReturn) -> NoReturn: ...
  93. def __rsub__(self, other: Any):
  94. """Subtract two numbers.
  95. Args:
  96. other: The other number.
  97. Returns:
  98. The number subtraction operation.
  99. """
  100. if not isinstance(other, NUMBER_TYPES):
  101. raise_unsupported_operand_types("-", (type(other), type(self)))
  102. return number_subtract_operation(+other, self)
  103. def __abs__(self):
  104. """Get the absolute value of the number.
  105. Returns:
  106. The number absolute operation.
  107. """
  108. return number_abs_operation(self)
  109. @overload
  110. def __mul__(self, other: number_types | boolean_types) -> NumberVar: ...
  111. @overload
  112. def __mul__(self, other: list | tuple | set | ArrayVar) -> ArrayVar: ...
  113. def __mul__(self, other: Any):
  114. """Multiply two numbers.
  115. Args:
  116. other: The other number.
  117. Returns:
  118. The number multiplication operation.
  119. """
  120. from .sequence import ArrayVar, LiteralArrayVar
  121. if isinstance(other, (list, tuple, set, ArrayVar)):
  122. if isinstance(other, ArrayVar):
  123. return other * self
  124. return LiteralArrayVar.create(other) * self
  125. if not isinstance(other, NUMBER_TYPES):
  126. raise_unsupported_operand_types("*", (type(self), type(other)))
  127. return number_multiply_operation(self, +other)
  128. @overload
  129. def __rmul__(self, other: number_types | boolean_types) -> NumberVar: ...
  130. @overload
  131. def __rmul__(self, other: list | tuple | set | ArrayVar) -> ArrayVar: ...
  132. def __rmul__(self, other: Any):
  133. """Multiply two numbers.
  134. Args:
  135. other: The other number.
  136. Returns:
  137. The number multiplication operation.
  138. """
  139. from .sequence import ArrayVar, LiteralArrayVar
  140. if isinstance(other, (list, tuple, set, ArrayVar)):
  141. if isinstance(other, ArrayVar):
  142. return other * self
  143. return LiteralArrayVar.create(other) * self
  144. if not isinstance(other, NUMBER_TYPES):
  145. raise_unsupported_operand_types("*", (type(other), type(self)))
  146. return number_multiply_operation(+other, self)
  147. @overload
  148. def __truediv__(self, other: number_types) -> NumberVar: ...
  149. @overload
  150. def __truediv__(self, other: NoReturn) -> NoReturn: ...
  151. def __truediv__(self, other: Any):
  152. """Divide two numbers.
  153. Args:
  154. other: The other number.
  155. Returns:
  156. The number true division operation.
  157. """
  158. if not isinstance(other, NUMBER_TYPES):
  159. raise_unsupported_operand_types("/", (type(self), type(other)))
  160. return number_true_division_operation(self, +other)
  161. @overload
  162. def __rtruediv__(self, other: number_types) -> NumberVar: ...
  163. @overload
  164. def __rtruediv__(self, other: NoReturn) -> NoReturn: ...
  165. def __rtruediv__(self, other: Any):
  166. """Divide two numbers.
  167. Args:
  168. other: The other number.
  169. Returns:
  170. The number true division operation.
  171. """
  172. if not isinstance(other, NUMBER_TYPES):
  173. raise_unsupported_operand_types("/", (type(other), type(self)))
  174. return number_true_division_operation(+other, self)
  175. @overload
  176. def __floordiv__(self, other: number_types) -> NumberVar: ...
  177. @overload
  178. def __floordiv__(self, other: NoReturn) -> NoReturn: ...
  179. def __floordiv__(self, other: Any):
  180. """Floor divide two numbers.
  181. Args:
  182. other: The other number.
  183. Returns:
  184. The number floor division operation.
  185. """
  186. if not isinstance(other, NUMBER_TYPES):
  187. raise_unsupported_operand_types("//", (type(self), type(other)))
  188. return number_floor_division_operation(self, +other)
  189. @overload
  190. def __rfloordiv__(self, other: number_types) -> NumberVar: ...
  191. @overload
  192. def __rfloordiv__(self, other: NoReturn) -> NoReturn: ...
  193. def __rfloordiv__(self, other: Any):
  194. """Floor divide two numbers.
  195. Args:
  196. other: The other number.
  197. Returns:
  198. The number floor division operation.
  199. """
  200. if not isinstance(other, NUMBER_TYPES):
  201. raise_unsupported_operand_types("//", (type(other), type(self)))
  202. return number_floor_division_operation(+other, self)
  203. @overload
  204. def __mod__(self, other: number_types) -> NumberVar: ...
  205. @overload
  206. def __mod__(self, other: NoReturn) -> NoReturn: ...
  207. def __mod__(self, other: Any):
  208. """Modulo two numbers.
  209. Args:
  210. other: The other number.
  211. Returns:
  212. The number modulo operation.
  213. """
  214. if not isinstance(other, NUMBER_TYPES):
  215. raise_unsupported_operand_types("%", (type(self), type(other)))
  216. return number_modulo_operation(self, +other)
  217. @overload
  218. def __rmod__(self, other: number_types) -> NumberVar: ...
  219. @overload
  220. def __rmod__(self, other: NoReturn) -> NoReturn: ...
  221. def __rmod__(self, other: Any):
  222. """Modulo two numbers.
  223. Args:
  224. other: The other number.
  225. Returns:
  226. The number modulo operation.
  227. """
  228. if not isinstance(other, NUMBER_TYPES):
  229. raise_unsupported_operand_types("%", (type(other), type(self)))
  230. return number_modulo_operation(+other, self)
  231. @overload
  232. def __pow__(self, other: number_types) -> NumberVar: ...
  233. @overload
  234. def __pow__(self, other: NoReturn) -> NoReturn: ...
  235. def __pow__(self, other: Any):
  236. """Exponentiate two numbers.
  237. Args:
  238. other: The other number.
  239. Returns:
  240. The number exponent operation.
  241. """
  242. if not isinstance(other, NUMBER_TYPES):
  243. raise_unsupported_operand_types("**", (type(self), type(other)))
  244. return number_exponent_operation(self, +other)
  245. @overload
  246. def __rpow__(self, other: number_types) -> NumberVar: ...
  247. @overload
  248. def __rpow__(self, other: NoReturn) -> NoReturn: ...
  249. def __rpow__(self, other: Any):
  250. """Exponentiate two numbers.
  251. Args:
  252. other: The other number.
  253. Returns:
  254. The number exponent operation.
  255. """
  256. if not isinstance(other, NUMBER_TYPES):
  257. raise_unsupported_operand_types("**", (type(other), type(self)))
  258. return number_exponent_operation(+other, self)
  259. def __neg__(self):
  260. """Negate the number.
  261. Returns:
  262. The number negation operation.
  263. """
  264. return number_negate_operation(self)
  265. def __invert__(self):
  266. """Boolean NOT the number.
  267. Returns:
  268. The boolean NOT operation.
  269. """
  270. return boolean_not_operation(self.bool())
  271. def __pos__(self) -> NumberVar:
  272. """Positive the number.
  273. Returns:
  274. The number.
  275. """
  276. return self
  277. def __round__(self):
  278. """Round the number.
  279. Returns:
  280. The number round operation.
  281. """
  282. return number_round_operation(self)
  283. def __ceil__(self):
  284. """Ceil the number.
  285. Returns:
  286. The number ceil operation.
  287. """
  288. return number_ceil_operation(self)
  289. def __floor__(self):
  290. """Floor the number.
  291. Returns:
  292. The number floor operation.
  293. """
  294. return number_floor_operation(self)
  295. def __trunc__(self):
  296. """Trunc the number.
  297. Returns:
  298. The number trunc operation.
  299. """
  300. return number_trunc_operation(self)
  301. @overload
  302. def __lt__(self, other: number_types) -> BooleanVar: ...
  303. @overload
  304. def __lt__(self, other: NoReturn) -> NoReturn: ...
  305. def __lt__(self, other: Any):
  306. """Less than comparison.
  307. Args:
  308. other: The other number.
  309. Returns:
  310. The result of the comparison.
  311. """
  312. if not isinstance(other, NUMBER_TYPES):
  313. raise_unsupported_operand_types("<", (type(self), type(other)))
  314. return less_than_operation(+self, +other)
  315. @overload
  316. def __le__(self, other: number_types) -> BooleanVar: ...
  317. @overload
  318. def __le__(self, other: NoReturn) -> NoReturn: ...
  319. def __le__(self, other: Any):
  320. """Less than or equal comparison.
  321. Args:
  322. other: The other number.
  323. Returns:
  324. The result of the comparison.
  325. """
  326. if not isinstance(other, NUMBER_TYPES):
  327. raise_unsupported_operand_types("<=", (type(self), type(other)))
  328. return less_than_or_equal_operation(+self, +other)
  329. def __eq__(self, other: Any):
  330. """Equal comparison.
  331. Args:
  332. other: The other number.
  333. Returns:
  334. The result of the comparison.
  335. """
  336. if isinstance(other, NUMBER_TYPES):
  337. return equal_operation(+self, +other)
  338. return equal_operation(self, other)
  339. def __ne__(self, other: Any):
  340. """Not equal comparison.
  341. Args:
  342. other: The other number.
  343. Returns:
  344. The result of the comparison.
  345. """
  346. if isinstance(other, NUMBER_TYPES):
  347. return not_equal_operation(+self, +other)
  348. return not_equal_operation(self, other)
  349. @overload
  350. def __gt__(self, other: number_types) -> BooleanVar: ...
  351. @overload
  352. def __gt__(self, other: NoReturn) -> NoReturn: ...
  353. def __gt__(self, other: Any):
  354. """Greater than comparison.
  355. Args:
  356. other: The other number.
  357. Returns:
  358. The result of the comparison.
  359. """
  360. if not isinstance(other, NUMBER_TYPES):
  361. raise_unsupported_operand_types(">", (type(self), type(other)))
  362. return greater_than_operation(+self, +other)
  363. @overload
  364. def __ge__(self, other: number_types) -> BooleanVar: ...
  365. @overload
  366. def __ge__(self, other: NoReturn) -> NoReturn: ...
  367. def __ge__(self, other: Any):
  368. """Greater than or equal comparison.
  369. Args:
  370. other: The other number.
  371. Returns:
  372. The result of the comparison.
  373. """
  374. if not isinstance(other, NUMBER_TYPES):
  375. raise_unsupported_operand_types(">=", (type(self), type(other)))
  376. return greater_than_or_equal_operation(+self, +other)
  377. def _is_strict_float(self) -> bool:
  378. """Check if the number is a float.
  379. Returns:
  380. bool: True if the number is a float.
  381. """
  382. return issubclass(self._var_type, float)
  383. def _is_strict_int(self) -> bool:
  384. """Check if the number is an int.
  385. Returns:
  386. bool: True if the number is an int.
  387. """
  388. return issubclass(self._var_type, int)
  389. def binary_number_operation(
  390. func: Callable[[NumberVar, NumberVar], str],
  391. ) -> Callable[[number_types, number_types], NumberVar]:
  392. """Decorator to create a binary number operation.
  393. Args:
  394. func: The binary number operation function.
  395. Returns:
  396. The binary number operation.
  397. """
  398. @var_operation
  399. def operation(lhs: NumberVar, rhs: NumberVar):
  400. return var_operation_return(
  401. js_expression=func(lhs, rhs),
  402. var_type=unionize(lhs._var_type, rhs._var_type),
  403. )
  404. def wrapper(lhs: number_types, rhs: number_types) -> NumberVar:
  405. """Create the binary number operation.
  406. Args:
  407. lhs: The first number.
  408. rhs: The second number.
  409. Returns:
  410. The binary number operation.
  411. """
  412. return operation(lhs, rhs) # type: ignore
  413. return wrapper
  414. @binary_number_operation
  415. def number_add_operation(lhs: NumberVar, rhs: NumberVar):
  416. """Add two numbers.
  417. Args:
  418. lhs: The first number.
  419. rhs: The second number.
  420. Returns:
  421. The number addition operation.
  422. """
  423. return f"({lhs} + {rhs})"
  424. @binary_number_operation
  425. def number_subtract_operation(lhs: NumberVar, rhs: NumberVar):
  426. """Subtract two numbers.
  427. Args:
  428. lhs: The first number.
  429. rhs: The second number.
  430. Returns:
  431. The number subtraction operation.
  432. """
  433. return f"({lhs} - {rhs})"
  434. @var_operation
  435. def number_abs_operation(value: NumberVar):
  436. """Get the absolute value of the number.
  437. Args:
  438. value: The number.
  439. Returns:
  440. The number absolute operation.
  441. """
  442. return var_operation_return(
  443. js_expression=f"Math.abs({value})", var_type=value._var_type
  444. )
  445. @binary_number_operation
  446. def number_multiply_operation(lhs: NumberVar, rhs: NumberVar):
  447. """Multiply two numbers.
  448. Args:
  449. lhs: The first number.
  450. rhs: The second number.
  451. Returns:
  452. The number multiplication operation.
  453. """
  454. return f"({lhs} * {rhs})"
  455. @var_operation
  456. def number_negate_operation(
  457. value: NumberVar[NUMBER_T],
  458. ) -> CustomVarOperationReturn[NUMBER_T]:
  459. """Negate the number.
  460. Args:
  461. value: The number.
  462. Returns:
  463. The number negation operation.
  464. """
  465. return var_operation_return(js_expression=f"-({value})", var_type=value._var_type)
  466. @binary_number_operation
  467. def number_true_division_operation(lhs: NumberVar, rhs: NumberVar):
  468. """Divide two numbers.
  469. Args:
  470. lhs: The first number.
  471. rhs: The second number.
  472. Returns:
  473. The number true division operation.
  474. """
  475. return f"({lhs} / {rhs})"
  476. @binary_number_operation
  477. def number_floor_division_operation(lhs: NumberVar, rhs: NumberVar):
  478. """Floor divide two numbers.
  479. Args:
  480. lhs: The first number.
  481. rhs: The second number.
  482. Returns:
  483. The number floor division operation.
  484. """
  485. return f"Math.floor({lhs} / {rhs})"
  486. @binary_number_operation
  487. def number_modulo_operation(lhs: NumberVar, rhs: NumberVar):
  488. """Modulo two numbers.
  489. Args:
  490. lhs: The first number.
  491. rhs: The second number.
  492. Returns:
  493. The number modulo operation.
  494. """
  495. return f"({lhs} % {rhs})"
  496. @binary_number_operation
  497. def number_exponent_operation(lhs: NumberVar, rhs: NumberVar):
  498. """Exponentiate two numbers.
  499. Args:
  500. lhs: The first number.
  501. rhs: The second number.
  502. Returns:
  503. The number exponent operation.
  504. """
  505. return f"({lhs} ** {rhs})"
  506. @var_operation
  507. def number_round_operation(value: NumberVar):
  508. """Round the number.
  509. Args:
  510. value: The number.
  511. Returns:
  512. The number round operation.
  513. """
  514. return var_operation_return(js_expression=f"Math.round({value})", var_type=int)
  515. @var_operation
  516. def number_ceil_operation(value: NumberVar):
  517. """Ceil the number.
  518. Args:
  519. value: The number.
  520. Returns:
  521. The number ceil operation.
  522. """
  523. return var_operation_return(js_expression=f"Math.ceil({value})", var_type=int)
  524. @var_operation
  525. def number_floor_operation(value: NumberVar):
  526. """Floor the number.
  527. Args:
  528. value: The number.
  529. Returns:
  530. The number floor operation.
  531. """
  532. return var_operation_return(js_expression=f"Math.floor({value})", var_type=int)
  533. @var_operation
  534. def number_trunc_operation(value: NumberVar):
  535. """Trunc the number.
  536. Args:
  537. value: The number.
  538. Returns:
  539. The number trunc operation.
  540. """
  541. return var_operation_return(js_expression=f"Math.trunc({value})", var_type=int)
  542. class BooleanVar(NumberVar[bool], python_types=bool):
  543. """Base class for immutable boolean vars."""
  544. def __invert__(self):
  545. """NOT the boolean.
  546. Returns:
  547. The boolean NOT operation.
  548. """
  549. return boolean_not_operation(self)
  550. def __int__(self):
  551. """Convert the boolean to an int.
  552. Returns:
  553. The boolean to int operation.
  554. """
  555. return boolean_to_number_operation(self)
  556. def __pos__(self):
  557. """Convert the boolean to an int.
  558. Returns:
  559. The boolean to int operation.
  560. """
  561. return boolean_to_number_operation(self)
  562. def bool(self) -> BooleanVar:
  563. """Boolean conversion.
  564. Returns:
  565. The boolean value of the boolean.
  566. """
  567. return self
  568. def __lt__(self, other: Any):
  569. """Less than comparison.
  570. Args:
  571. other: The other boolean.
  572. Returns:
  573. The result of the comparison.
  574. """
  575. return +self < other
  576. def __le__(self, other: Any):
  577. """Less than or equal comparison.
  578. Args:
  579. other: The other boolean.
  580. Returns:
  581. The result of the comparison.
  582. """
  583. return +self <= other
  584. def __gt__(self, other: Any):
  585. """Greater than comparison.
  586. Args:
  587. other: The other boolean.
  588. Returns:
  589. The result of the comparison.
  590. """
  591. return +self > other
  592. def __ge__(self, other: Any):
  593. """Greater than or equal comparison.
  594. Args:
  595. other: The other boolean.
  596. Returns:
  597. The result of the comparison.
  598. """
  599. return +self >= other
  600. @var_operation
  601. def boolean_to_number_operation(value: BooleanVar):
  602. """Convert the boolean to a number.
  603. Args:
  604. value: The boolean.
  605. Returns:
  606. The boolean to number operation.
  607. """
  608. return var_operation_return(js_expression=f"Number({value})", var_type=int)
  609. def comparison_operator(
  610. func: Callable[[Var, Var], str],
  611. ) -> Callable[[Var | Any, Var | Any], BooleanVar]:
  612. """Decorator to create a comparison operation.
  613. Args:
  614. func: The comparison operation function.
  615. Returns:
  616. The comparison operation.
  617. """
  618. @var_operation
  619. def operation(lhs: Var, rhs: Var):
  620. return var_operation_return(
  621. js_expression=func(lhs, rhs),
  622. var_type=bool,
  623. )
  624. def wrapper(lhs: Var | Any, rhs: Var | Any) -> BooleanVar:
  625. """Create the comparison operation.
  626. Args:
  627. lhs: The first value.
  628. rhs: The second value.
  629. Returns:
  630. The comparison operation.
  631. """
  632. return operation(lhs, rhs)
  633. return wrapper
  634. @comparison_operator
  635. def greater_than_operation(lhs: Var, rhs: Var):
  636. """Greater than comparison.
  637. Args:
  638. lhs: The first value.
  639. rhs: The second value.
  640. Returns:
  641. The result of the comparison.
  642. """
  643. return f"({lhs} > {rhs})"
  644. @comparison_operator
  645. def greater_than_or_equal_operation(lhs: Var, rhs: Var):
  646. """Greater than or equal comparison.
  647. Args:
  648. lhs: The first value.
  649. rhs: The second value.
  650. Returns:
  651. The result of the comparison.
  652. """
  653. return f"({lhs} >= {rhs})"
  654. @comparison_operator
  655. def less_than_operation(lhs: Var, rhs: Var):
  656. """Less than comparison.
  657. Args:
  658. lhs: The first value.
  659. rhs: The second value.
  660. Returns:
  661. The result of the comparison.
  662. """
  663. return f"({lhs} < {rhs})"
  664. @comparison_operator
  665. def less_than_or_equal_operation(lhs: Var, rhs: Var):
  666. """Less than or equal comparison.
  667. Args:
  668. lhs: The first value.
  669. rhs: The second value.
  670. Returns:
  671. The result of the comparison.
  672. """
  673. return f"({lhs} <= {rhs})"
  674. @comparison_operator
  675. def equal_operation(lhs: Var, rhs: Var):
  676. """Equal comparison.
  677. Args:
  678. lhs: The first value.
  679. rhs: The second value.
  680. Returns:
  681. The result of the comparison.
  682. """
  683. return f"({lhs} === {rhs})"
  684. @comparison_operator
  685. def not_equal_operation(lhs: Var, rhs: Var):
  686. """Not equal comparison.
  687. Args:
  688. lhs: The first value.
  689. rhs: The second value.
  690. Returns:
  691. The result of the comparison.
  692. """
  693. return f"({lhs} !== {rhs})"
  694. @var_operation
  695. def boolean_not_operation(value: BooleanVar):
  696. """Boolean NOT the boolean.
  697. Args:
  698. value: The boolean.
  699. Returns:
  700. The boolean NOT operation.
  701. """
  702. return var_operation_return(js_expression=f"!({value})", var_type=bool)
  703. @dataclasses.dataclass(
  704. eq=False,
  705. frozen=True,
  706. **{"slots": True} if sys.version_info >= (3, 10) else {},
  707. )
  708. class LiteralNumberVar(LiteralVar, NumberVar):
  709. """Base class for immutable literal number vars."""
  710. _var_value: float | int = dataclasses.field(default=0)
  711. def json(self) -> str:
  712. """Get the JSON representation of the var.
  713. Returns:
  714. The JSON representation of the var.
  715. Raises:
  716. PrimitiveUnserializableToJSONError: If the var is unserializable to JSON.
  717. """
  718. if math.isinf(self._var_value) or math.isnan(self._var_value):
  719. raise PrimitiveUnserializableToJSONError(
  720. f"No valid JSON representation for {self}"
  721. )
  722. return json.dumps(self._var_value)
  723. def __hash__(self) -> int:
  724. """Calculate the hash value of the object.
  725. Returns:
  726. int: The hash value of the object.
  727. """
  728. return hash((type(self).__name__, self._var_value))
  729. @classmethod
  730. def create(cls, value: float | int, _var_data: VarData | None = None):
  731. """Create the number var.
  732. Args:
  733. value: The value of the var.
  734. _var_data: Additional hooks and imports associated with the Var.
  735. Returns:
  736. The number var.
  737. """
  738. if math.isinf(value):
  739. js_expr = "Infinity" if value > 0 else "-Infinity"
  740. elif math.isnan(value):
  741. js_expr = "NaN"
  742. else:
  743. js_expr = str(value)
  744. return cls(
  745. _js_expr=js_expr,
  746. _var_type=type(value),
  747. _var_data=_var_data,
  748. _var_value=value,
  749. )
  750. @dataclasses.dataclass(
  751. eq=False,
  752. frozen=True,
  753. **{"slots": True} if sys.version_info >= (3, 10) else {},
  754. )
  755. class LiteralBooleanVar(LiteralVar, BooleanVar):
  756. """Base class for immutable literal boolean vars."""
  757. _var_value: bool = dataclasses.field(default=False)
  758. def json(self) -> str:
  759. """Get the JSON representation of the var.
  760. Returns:
  761. The JSON representation of the var.
  762. """
  763. return "true" if self._var_value else "false"
  764. def __hash__(self) -> int:
  765. """Calculate the hash value of the object.
  766. Returns:
  767. int: The hash value of the object.
  768. """
  769. return hash((type(self).__name__, self._var_value))
  770. @classmethod
  771. def create(cls, value: bool, _var_data: VarData | None = None):
  772. """Create the boolean var.
  773. Args:
  774. value: The value of the var.
  775. _var_data: Additional hooks and imports associated with the Var.
  776. Returns:
  777. The boolean var.
  778. """
  779. return cls(
  780. _js_expr="true" if value else "false",
  781. _var_type=bool,
  782. _var_data=_var_data,
  783. _var_value=value,
  784. )
  785. number_types = Union[NumberVar, int, float]
  786. boolean_types = Union[BooleanVar, bool]
  787. _IS_TRUE_IMPORT: ImportDict = {
  788. f"$/{Dirs.STATE_PATH}": [ImportVar(tag="isTrue")],
  789. }
  790. @var_operation
  791. def boolify(value: Var):
  792. """Convert the value to a boolean.
  793. Args:
  794. value: The value.
  795. Returns:
  796. The boolean value.
  797. """
  798. return var_operation_return(
  799. js_expression=f"isTrue({value})",
  800. var_type=bool,
  801. var_data=VarData(imports=_IS_TRUE_IMPORT),
  802. )
  803. T = TypeVar("T")
  804. U = TypeVar("U")
  805. @var_operation
  806. def ternary_operation(
  807. condition: BooleanVar, if_true: Var[T], if_false: Var[U]
  808. ) -> CustomVarOperationReturn[Union[T, U]]:
  809. """Create a ternary operation.
  810. Args:
  811. condition: The condition.
  812. if_true: The value if the condition is true.
  813. if_false: The value if the condition is false.
  814. Returns:
  815. The ternary operation.
  816. """
  817. type_value: Union[Type[T], Type[U]] = unionize(
  818. if_true._var_type, if_false._var_type
  819. )
  820. value: CustomVarOperationReturn[Union[T, U]] = var_operation_return(
  821. js_expression=f"({condition} ? {if_true} : {if_false})",
  822. var_type=type_value,
  823. )
  824. return value
  825. NUMBER_TYPES = (int, float, NumberVar)