123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182 |
- """Immutable number vars."""
- from __future__ import annotations
- import dataclasses
- import functools
- import json
- import math
- from typing import (
- TYPE_CHECKING,
- Any,
- Callable,
- NoReturn,
- Sequence,
- TypeVar,
- Union,
- cast,
- overload,
- )
- from typing_extensions import Unpack
- from reflex.constants.base import Dirs
- from reflex.utils.exceptions import PrimitiveUnserializableToJSONError, VarTypeError
- from reflex.utils.imports import ImportDict, ImportVar
- from .base import (
- VAR_TYPE,
- CachedVarOperation,
- CustomVarOperationReturn,
- LiteralVar,
- ReflexCallable,
- Var,
- VarData,
- cached_property_no_lock,
- nary_type_computer,
- passthrough_unary_type_computer,
- unionize,
- var_operation,
- var_operation_return,
- )
- NUMBER_T = TypeVar("NUMBER_T", int, float, bool)
- if TYPE_CHECKING:
- from .function import FunctionVar
- from .sequence import ArrayVar
- def raise_unsupported_operand_types(
- operator: str, operands_types: tuple[type, ...]
- ) -> NoReturn:
- """Raise an unsupported operand types error.
- Args:
- operator: The operator.
- operands_types: The types of the operands.
- Raises:
- VarTypeError: The operand types are unsupported.
- """
- raise VarTypeError(
- f"Unsupported Operand type(s) for {operator}: {', '.join(t.__name__ for t in operands_types)}"
- )
- class NumberVar(Var[NUMBER_T], python_types=(int, float)):
- """Base class for immutable number vars."""
- def __add__(self, other: number_types) -> NumberVar:
- """Add two numbers.
- Args:
- other: The other number.
- Returns:
- The number addition operation.
- """
- if not isinstance(other, NUMBER_TYPES):
- raise_unsupported_operand_types("+", (type(self), type(other)))
- return number_add_operation(self, +other).guess_type()
- def __radd__(self, other: number_types) -> NumberVar:
- """Add two numbers.
- Args:
- other: The other number.
- Returns:
- The number addition operation.
- """
- if not isinstance(other, NUMBER_TYPES):
- raise_unsupported_operand_types("+", (type(other), type(self)))
- return number_add_operation(+other, self).guess_type()
- def __sub__(self, other: number_types) -> NumberVar:
- """Subtract two numbers.
- Args:
- other: The other number.
- Returns:
- The number subtraction operation.
- """
- if not isinstance(other, NUMBER_TYPES):
- raise_unsupported_operand_types("-", (type(self), type(other)))
- return number_subtract_operation(self, +other).guess_type()
- def __rsub__(self, other: number_types) -> NumberVar:
- """Subtract two numbers.
- Args:
- other: The other number.
- Returns:
- The number subtraction operation.
- """
- if not isinstance(other, NUMBER_TYPES):
- raise_unsupported_operand_types("-", (type(other), type(self)))
- return number_subtract_operation(+other, self).guess_type()
- def __abs__(self):
- """Get the absolute value of the number.
- Returns:
- The number absolute operation.
- """
- return number_abs_operation(self)
- @overload
- def __mul__(self, other: number_types | boolean_types) -> NumberVar: ...
- @overload
- def __mul__(self, other: list | tuple | set | ArrayVar) -> ArrayVar: ...
- def __mul__(self, other: Any):
- """Multiply two numbers.
- Args:
- other: The other number.
- Returns:
- The number multiplication operation.
- """
- from .sequence import ArrayVar, LiteralArrayVar
- if isinstance(other, (list, tuple, ArrayVar)):
- if isinstance(other, ArrayVar):
- return other * self
- return LiteralArrayVar.create(other) * self
- if not isinstance(other, NUMBER_TYPES):
- raise_unsupported_operand_types("*", (type(self), type(other)))
- return number_multiply_operation(self, +other).guess_type()
- @overload
- def __rmul__(self, other: number_types | boolean_types) -> NumberVar: ...
- @overload
- def __rmul__(self, other: list | tuple | set | ArrayVar) -> ArrayVar: ...
- def __rmul__(self, other: Any):
- """Multiply two numbers.
- Args:
- other: The other number.
- Returns:
- The number multiplication operation.
- """
- from .sequence import ArrayVar, LiteralArrayVar
- if isinstance(other, (list, tuple, ArrayVar)):
- if isinstance(other, ArrayVar):
- return other * self
- return LiteralArrayVar.create(other) * self
- if not isinstance(other, NUMBER_TYPES):
- raise_unsupported_operand_types("*", (type(other), type(self)))
- return number_multiply_operation(+other, self).guess_type()
- def __truediv__(self, other: number_types) -> NumberVar:
- """Divide two numbers.
- Args:
- other: The other number.
- Returns:
- The number true division operation.
- """
- if not isinstance(other, NUMBER_TYPES):
- raise_unsupported_operand_types("/", (type(self), type(other)))
- return number_true_division_operation(self, +other).guess_type()
- def __rtruediv__(self, other: number_types) -> NumberVar:
- """Divide two numbers.
- Args:
- other: The other number.
- Returns:
- The number true division operation.
- """
- if not isinstance(other, NUMBER_TYPES):
- raise_unsupported_operand_types("/", (type(other), type(self)))
- return number_true_division_operation(+other, self).guess_type()
- def __floordiv__(self, other: number_types) -> NumberVar:
- """Floor divide two numbers.
- Args:
- other: The other number.
- Returns:
- The number floor division operation.
- """
- if not isinstance(other, NUMBER_TYPES):
- raise_unsupported_operand_types("//", (type(self), type(other)))
- return number_floor_division_operation(self, +other).guess_type()
- def __rfloordiv__(self, other: number_types) -> NumberVar:
- """Floor divide two numbers.
- Args:
- other: The other number.
- Returns:
- The number floor division operation.
- """
- if not isinstance(other, NUMBER_TYPES):
- raise_unsupported_operand_types("//", (type(other), type(self)))
- return number_floor_division_operation(+other, self).guess_type()
- def __mod__(self, other: number_types) -> NumberVar:
- """Modulo two numbers.
- Args:
- other: The other number.
- Returns:
- The number modulo operation.
- """
- if not isinstance(other, NUMBER_TYPES):
- raise_unsupported_operand_types("%", (type(self), type(other)))
- return number_modulo_operation(self, +other).guess_type()
- def __rmod__(self, other: number_types) -> NumberVar:
- """Modulo two numbers.
- Args:
- other: The other number.
- Returns:
- The number modulo operation.
- """
- if not isinstance(other, NUMBER_TYPES):
- raise_unsupported_operand_types("%", (type(other), type(self)))
- return number_modulo_operation(+other, self).guess_type()
- def __pow__(self, other: number_types) -> NumberVar:
- """Exponentiate two numbers.
- Args:
- other: The other number.
- Returns:
- The number exponent operation.
- """
- if not isinstance(other, NUMBER_TYPES):
- raise_unsupported_operand_types("**", (type(self), type(other)))
- return number_exponent_operation(self, +other).guess_type()
- def __rpow__(self, other: number_types) -> NumberVar:
- """Exponentiate two numbers.
- Args:
- other: The other number.
- Returns:
- The number exponent operation.
- """
- if not isinstance(other, NUMBER_TYPES):
- raise_unsupported_operand_types("**", (type(other), type(self)))
- return number_exponent_operation(+other, self).guess_type()
- def __neg__(self):
- """Negate the number.
- Returns:
- The number negation operation.
- """
- return number_negate_operation(self).guess_type()
- def __invert__(self):
- """Boolean NOT the number.
- Returns:
- The boolean NOT operation.
- """
- return boolean_not_operation(self.bool()).guess_type()
- def __pos__(self) -> NumberVar:
- """Positive the number.
- Returns:
- The number.
- """
- return self
- def __round__(self):
- """Round the number.
- Returns:
- The number round operation.
- """
- return number_round_operation(self).guess_type()
- def __ceil__(self):
- """Ceil the number.
- Returns:
- The number ceil operation.
- """
- return number_ceil_operation(self).guess_type()
- def __floor__(self):
- """Floor the number.
- Returns:
- The number floor operation.
- """
- return number_floor_operation(self).guess_type()
- def __trunc__(self):
- """Trunc the number.
- Returns:
- The number trunc operation.
- """
- return number_trunc_operation(self).guess_type()
- def __lt__(self, other: number_types) -> BooleanVar:
- """Less than comparison.
- Args:
- other: The other number.
- Returns:
- The result of the comparison.
- """
- if not isinstance(other, NUMBER_TYPES):
- raise_unsupported_operand_types("<", (type(self), type(other)))
- return less_than_operation(+self, +other).guess_type()
- def __le__(self, other: number_types) -> BooleanVar:
- """Less than or equal comparison.
- Args:
- other: The other number.
- Returns:
- The result of the comparison.
- """
- if not isinstance(other, NUMBER_TYPES):
- raise_unsupported_operand_types("<=", (type(self), type(other)))
- return less_than_or_equal_operation(+self, +other).guess_type()
- def __eq__(self, other: Any) -> BooleanVar:
- """Equal comparison.
- Args:
- other: The other number.
- Returns:
- The result of the comparison.
- """
- if isinstance(other, NUMBER_TYPES):
- return equal_operation(+self, +other).guess_type()
- return equal_operation(self, other).guess_type()
- def __ne__(self, other: Any) -> BooleanVar:
- """Not equal comparison.
- Args:
- other: The other number.
- Returns:
- The result of the comparison.
- """
- if isinstance(other, NUMBER_TYPES):
- return not_equal_operation(+self, +other).guess_type()
- return not_equal_operation(self, other).guess_type()
- def __gt__(self, other: number_types) -> BooleanVar:
- """Greater than comparison.
- Args:
- other: The other number.
- Returns:
- The result of the comparison.
- """
- if not isinstance(other, NUMBER_TYPES):
- raise_unsupported_operand_types(">", (type(self), type(other)))
- return greater_than_operation(+self, +other).guess_type()
- def __ge__(self, other: number_types) -> BooleanVar:
- """Greater than or equal comparison.
- Args:
- other: The other number.
- Returns:
- The result of the comparison.
- """
- if not isinstance(other, NUMBER_TYPES):
- raise_unsupported_operand_types(">=", (type(self), type(other)))
- return greater_than_or_equal_operation(+self, +other).guess_type()
- def _is_strict_float(self) -> bool:
- """Check if the number is a float.
- Returns:
- bool: True if the number is a float.
- """
- return issubclass(self._var_type, float)
- def _is_strict_int(self) -> bool:
- """Check if the number is an int.
- Returns:
- bool: True if the number is an int.
- """
- return issubclass(self._var_type, int)
- def binary_number_operation(
- func: Callable[[Var[int | float], Var[int | float]], str],
- ):
- """Decorator to create a binary number operation.
- Args:
- func: The binary number operation function.
- Returns:
- The binary number operation.
- """
- def operation(
- lhs: Var[int | float], rhs: Var[int | float]
- ) -> CustomVarOperationReturn[int | float]:
- def type_computer(*args: Var):
- if not args:
- return (
- ReflexCallable[[int | float, int | float], int | float],
- type_computer,
- )
- if len(args) == 1:
- return (
- ReflexCallable[[int | float], int | float],
- functools.partial(type_computer, args[0]),
- )
- return (
- ReflexCallable[[], unionize(args[0]._var_type, args[1]._var_type)],
- None,
- )
- return var_operation_return(
- js_expression=func(lhs, rhs),
- type_computer=type_computer,
- )
- operation.__name__ = func.__name__
- return var_operation(operation)
- @binary_number_operation
- def number_add_operation(lhs: Var[int | float], rhs: Var[int | float]):
- """Add two numbers.
- Args:
- lhs: The first number.
- rhs: The second number.
- Returns:
- The number addition operation.
- """
- return f"({lhs} + {rhs})"
- @binary_number_operation
- def number_subtract_operation(lhs: Var[int | float], rhs: Var[int | float]):
- """Subtract two numbers.
- Args:
- lhs: The first number.
- rhs: The second number.
- Returns:
- The number subtraction operation.
- """
- return f"({lhs} - {rhs})"
- unary_operation_type_computer = passthrough_unary_type_computer(
- ReflexCallable[[int | float], int | float]
- )
- @var_operation
- def number_abs_operation(
- value: Var[int | float],
- ) -> CustomVarOperationReturn[int | float]:
- """Get the absolute value of the number.
- Args:
- value: The number.
- Returns:
- The number absolute operation.
- """
- return var_operation_return(
- js_expression=f"Math.abs({value})",
- type_computer=unary_operation_type_computer,
- _raw_js_function="Math.abs",
- )
- @binary_number_operation
- def number_multiply_operation(lhs: Var[int | float], rhs: Var[int | float]):
- """Multiply two numbers.
- Args:
- lhs: The first number.
- rhs: The second number.
- Returns:
- The number multiplication operation.
- """
- return f"({lhs} * {rhs})"
- @var_operation
- def number_negate_operation(
- value: Var[NUMBER_T],
- ) -> CustomVarOperationReturn[NUMBER_T]:
- """Negate the number.
- Args:
- value: The number.
- Returns:
- The number negation operation.
- """
- return var_operation_return(
- js_expression=f"-({value})", type_computer=unary_operation_type_computer
- )
- @binary_number_operation
- def number_true_division_operation(lhs: Var[int | float], rhs: Var[int | float]):
- """Divide two numbers.
- Args:
- lhs: The first number.
- rhs: The second number.
- Returns:
- The number true division operation.
- """
- return f"({lhs} / {rhs})"
- @binary_number_operation
- def number_floor_division_operation(lhs: Var[int | float], rhs: Var[int | float]):
- """Floor divide two numbers.
- Args:
- lhs: The first number.
- rhs: The second number.
- Returns:
- The number floor division operation.
- """
- return f"Math.floor({lhs} / {rhs})"
- @binary_number_operation
- def number_modulo_operation(lhs: Var[int | float], rhs: Var[int | float]):
- """Modulo two numbers.
- Args:
- lhs: The first number.
- rhs: The second number.
- Returns:
- The number modulo operation.
- """
- return f"({lhs} % {rhs})"
- @binary_number_operation
- def number_exponent_operation(lhs: Var[int | float], rhs: Var[int | float]):
- """Exponentiate two numbers.
- Args:
- lhs: The first number.
- rhs: The second number.
- Returns:
- The number exponent operation.
- """
- return f"({lhs} ** {rhs})"
- @var_operation
- def number_round_operation(value: Var[int | float]):
- """Round the number.
- Args:
- value: The number.
- Returns:
- The number round operation.
- """
- return var_operation_return(js_expression=f"Math.round({value})", var_type=int)
- @var_operation
- def number_ceil_operation(value: Var[int | float]):
- """Ceil the number.
- Args:
- value: The number.
- Returns:
- The number ceil operation.
- """
- return var_operation_return(js_expression=f"Math.ceil({value})", var_type=int)
- @var_operation
- def number_floor_operation(value: Var[int | float]):
- """Floor the number.
- Args:
- value: The number.
- Returns:
- The number floor operation.
- """
- return var_operation_return(
- js_expression=f"Math.floor({value})",
- var_type=int,
- _raw_js_function="Math.floor",
- )
- @var_operation
- def number_trunc_operation(value: Var[int | float]):
- """Trunc the number.
- Args:
- value: The number.
- Returns:
- The number trunc operation.
- """
- return var_operation_return(js_expression=f"Math.trunc({value})", var_type=int)
- class BooleanVar(NumberVar[bool], python_types=bool):
- """Base class for immutable boolean vars."""
- def __invert__(self):
- """NOT the boolean.
- Returns:
- The boolean NOT operation.
- """
- return boolean_not_operation(self).guess_type()
- def __int__(self):
- """Convert the boolean to an int.
- Returns:
- The boolean to int operation.
- """
- return boolean_to_number_operation(self).guess_type()
- def __pos__(self):
- """Convert the boolean to an int.
- Returns:
- The boolean to int operation.
- """
- return boolean_to_number_operation(self).guess_type()
- def bool(self) -> BooleanVar:
- """Boolean conversion.
- Returns:
- The boolean value of the boolean.
- """
- return self
- def __lt__(self, other: Any):
- """Less than comparison.
- Args:
- other: The other boolean.
- Returns:
- The result of the comparison.
- """
- return +self < other
- def __le__(self, other: Any):
- """Less than or equal comparison.
- Args:
- other: The other boolean.
- Returns:
- The result of the comparison.
- """
- return +self <= other
- def __gt__(self, other: Any):
- """Greater than comparison.
- Args:
- other: The other boolean.
- Returns:
- The result of the comparison.
- """
- return +self > other
- def __ge__(self, other: Any):
- """Greater than or equal comparison.
- Args:
- other: The other boolean.
- Returns:
- The result of the comparison.
- """
- return +self >= other
- @var_operation
- def boolean_to_number_operation(value: Var[bool]):
- """Convert the boolean to a number.
- Args:
- value: The boolean.
- Returns:
- The boolean to number operation.
- """
- return var_operation_return(
- js_expression=f"Number({value})", var_type=int, _raw_js_function="Number"
- )
- def comparison_operator(
- func: Callable[[Var, Var], str],
- ) -> FunctionVar[ReflexCallable[[Any, Any], bool]]:
- """Decorator to create a comparison operation.
- Args:
- func: The comparison operation function.
- Returns:
- The comparison operation.
- """
- def operation(lhs: Var[Any], rhs: Var[Any]):
- return var_operation_return(
- js_expression=func(lhs, rhs),
- var_type=bool,
- )
- operation.__name__ = func.__name__
- return var_operation(operation)
- @comparison_operator
- def greater_than_operation(lhs: Var, rhs: Var):
- """Greater than comparison.
- Args:
- lhs: The first value.
- rhs: The second value.
- Returns:
- The result of the comparison.
- """
- return f"({lhs} > {rhs})"
- @comparison_operator
- def greater_than_or_equal_operation(lhs: Var, rhs: Var):
- """Greater than or equal comparison.
- Args:
- lhs: The first value.
- rhs: The second value.
- Returns:
- The result of the comparison.
- """
- return f"({lhs} >= {rhs})"
- @comparison_operator
- def less_than_operation(lhs: Var, rhs: Var):
- """Less than comparison.
- Args:
- lhs: The first value.
- rhs: The second value.
- Returns:
- The result of the comparison.
- """
- return f"({lhs} < {rhs})"
- @comparison_operator
- def less_than_or_equal_operation(lhs: Var, rhs: Var):
- """Less than or equal comparison.
- Args:
- lhs: The first value.
- rhs: The second value.
- Returns:
- The result of the comparison.
- """
- return f"({lhs} <= {rhs})"
- @comparison_operator
- def equal_operation(lhs: Var, rhs: Var):
- """Equal comparison.
- Args:
- lhs: The first value.
- rhs: The second value.
- Returns:
- The result of the comparison.
- """
- return f"({lhs} === {rhs})"
- @comparison_operator
- def not_equal_operation(lhs: Var, rhs: Var):
- """Not equal comparison.
- Args:
- lhs: The first value.
- rhs: The second value.
- Returns:
- The result of the comparison.
- """
- return f"({lhs} !== {rhs})"
- @var_operation
- def boolean_not_operation(value: Var[bool]):
- """Boolean NOT the boolean.
- Args:
- value: The boolean.
- Returns:
- The boolean NOT operation.
- """
- return var_operation_return(js_expression=f"!({value})", var_type=bool)
- @dataclasses.dataclass(
- eq=False,
- frozen=True,
- slots=True,
- )
- class LiteralNumberVar(LiteralVar, NumberVar[NUMBER_T]):
- """Base class for immutable literal number vars."""
- _var_value: float | int = dataclasses.field(default=0)
- def json(self) -> str:
- """Get the JSON representation of the var.
- Returns:
- The JSON representation of the var.
- Raises:
- PrimitiveUnserializableToJSONError: If the var is unserializable to JSON.
- """
- if math.isinf(self._var_value) or math.isnan(self._var_value):
- raise PrimitiveUnserializableToJSONError(
- f"No valid JSON representation for {self}"
- )
- return json.dumps(self._var_value)
- def __hash__(self) -> int:
- """Calculate the hash value of the object.
- Returns:
- int: The hash value of the object.
- """
- return hash((type(self).__name__, self._var_value))
- @classmethod
- def create(cls, value: float | int, _var_data: VarData | None = None):
- """Create the number var.
- Args:
- value: The value of the var.
- _var_data: Additional hooks and imports associated with the Var.
- Returns:
- The number var.
- """
- if math.isinf(value):
- js_expr = "Infinity" if value > 0 else "-Infinity"
- elif math.isnan(value):
- js_expr = "NaN"
- else:
- js_expr = str(value)
- return cls(
- _js_expr=js_expr,
- _var_type=type(value),
- _var_data=_var_data,
- _var_value=value,
- )
- @dataclasses.dataclass(
- eq=False,
- frozen=True,
- slots=True,
- )
- class LiteralBooleanVar(LiteralVar, BooleanVar):
- """Base class for immutable literal boolean vars."""
- _var_value: bool = dataclasses.field(default=False)
- def json(self) -> str:
- """Get the JSON representation of the var.
- Returns:
- The JSON representation of the var.
- """
- return "true" if self._var_value else "false"
- def __hash__(self) -> int:
- """Calculate the hash value of the object.
- Returns:
- int: The hash value of the object.
- """
- return hash((type(self).__name__, self._var_value))
- @classmethod
- def create(cls, value: bool, _var_data: VarData | None = None):
- """Create the boolean var.
- Args:
- value: The value of the var.
- _var_data: Additional hooks and imports associated with the Var.
- Returns:
- The boolean var.
- """
- return cls(
- _js_expr="true" if value else "false",
- _var_type=bool,
- _var_data=_var_data,
- _var_value=value,
- )
- number_types = Union[NumberVar, int, float]
- boolean_types = Union[BooleanVar, bool]
- _IS_TRUE_IMPORT: ImportDict = {
- f"$/{Dirs.STATE_PATH}": [ImportVar(tag="isTrue")],
- }
- _AT_SLICE_IMPORT: ImportDict = {
- f"$/{Dirs.STATE_PATH}": [ImportVar(tag="atSlice")],
- }
- _AT_SLICE_OR_INDEX: ImportDict = {
- f"$/{Dirs.STATE_PATH}": [ImportVar(tag="atSliceOrIndex")],
- }
- _RANGE_IMPORT: ImportDict = {
- f"$/{Dirs.UTILS}/helpers/range": [ImportVar(tag="range", is_default=True)],
- }
- @var_operation
- def boolify(value: Var):
- """Convert the value to a boolean.
- Args:
- value: The value.
- Returns:
- The boolean value.
- """
- return var_operation_return(
- js_expression=f"isTrue({value})",
- var_type=bool,
- var_data=VarData(imports=_IS_TRUE_IMPORT),
- _raw_js_function="isTrue",
- )
- T = TypeVar("T", bound=Any)
- U = TypeVar("U", bound=Any)
- @var_operation
- def ternary_operation(
- condition: Var[bool], if_true: Var[T], if_false: Var[U]
- ) -> CustomVarOperationReturn[Union[T, U]]:
- """Create a ternary operation.
- Args:
- condition: The condition.
- if_true: The value if the condition is true.
- if_false: The value if the condition is false.
- Returns:
- The ternary operation.
- """
- value: CustomVarOperationReturn[Union[T, U]] = var_operation_return(
- js_expression=f"({condition} ? {if_true} : {if_false})",
- type_computer=nary_type_computer(
- ReflexCallable[[bool, Any, Any], Any],
- ReflexCallable[[Any, Any], Any],
- ReflexCallable[[Any], Any],
- computer=lambda args: unionize(args[1]._var_type, args[2]._var_type),
- ),
- )
- return value
- TUPLE_ENDS_IN_VAR = tuple[Unpack[tuple[Var[Any], ...]], Var[VAR_TYPE]]
- TUPLE_ENDS_IN_VAR_RELAXED = tuple[
- Unpack[tuple[Var[Any] | Any, ...]], Var[VAR_TYPE] | VAR_TYPE
- ]
- @dataclasses.dataclass(
- eq=False,
- frozen=True,
- slots=True,
- )
- class MatchOperation(CachedVarOperation, Var[VAR_TYPE]):
- """Base class for immutable match operations."""
- _cond: Var[bool] = dataclasses.field(
- default_factory=lambda: LiteralBooleanVar.create(True)
- )
- _cases: tuple[TUPLE_ENDS_IN_VAR[VAR_TYPE], ...] = dataclasses.field(
- default_factory=tuple
- )
- _default: Var[VAR_TYPE] = dataclasses.field( # pyright: ignore[reportAssignmentType]
- default_factory=lambda: Var.create(None)
- )
- @cached_property_no_lock
- def _cached_var_name(self) -> str:
- """Get the name of the var.
- Returns:
- The name of the var.
- """
- switch_code = f"(() => {{ switch (JSON.stringify({self._cond!s})) {{"
- for case in self._cases:
- conditions = case[:-1]
- return_value = case[-1]
- case_conditions = " ".join(
- [f"case JSON.stringify({condition!s}):" for condition in conditions]
- )
- case_code = f"{case_conditions} return ({return_value!s}); break;"
- switch_code += case_code
- switch_code += f"default: return ({self._default!s}); break;"
- switch_code += "};})()"
- return switch_code
- @cached_property_no_lock
- def _cached_get_all_var_data(self) -> VarData | None:
- """Get the VarData for the var.
- Returns:
- The VarData for the var.
- """
- return VarData.merge(
- self._cond._get_all_var_data(),
- *(
- cond_or_return._get_all_var_data()
- for case in self._cases
- for cond_or_return in case
- ),
- self._default._get_all_var_data(),
- self._var_data,
- )
- @classmethod
- def create(
- cls,
- cond: Any,
- cases: Sequence[TUPLE_ENDS_IN_VAR_RELAXED[VAR_TYPE]],
- default: Var[VAR_TYPE] | VAR_TYPE,
- _var_data: VarData | None = None,
- _var_type: type[VAR_TYPE] | None = None,
- ):
- """Create the match operation.
- Args:
- cond: The condition.
- cases: The cases.
- default: The default case.
- _var_data: Additional hooks and imports associated with the Var.
- _var_type: The type of the Var.
- Returns:
- The match operation.
- """
- cond = Var.create(cond)
- cases = cast(
- tuple[TUPLE_ENDS_IN_VAR[VAR_TYPE], ...],
- tuple(tuple(Var.create(c) for c in case) for case in cases),
- )
- _default = cast(Var[VAR_TYPE], Var.create(default))
- var_type = _var_type or unionize(
- *(case[-1]._var_type for case in cases),
- _default._var_type,
- )
- return cls(
- _js_expr="",
- _var_data=_var_data,
- _var_type=var_type,
- _cond=cond,
- _cases=cases,
- _default=_default,
- )
- NUMBER_TYPES = (int, float, NumberVar)
|