number.py 28 KB

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