number.py 27 KB

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