number.py 29 KB

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