123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709 |
- """Define a state var."""
- from __future__ import annotations
- import json
- from abc import ABC
- from typing import _GenericAlias # type: ignore
- from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Type, Union
- from plotly.graph_objects import Figure
- from plotly.io import to_json
- from pydantic.fields import ModelField
- from pynecone import constants, utils
- from pynecone.base import Base
- if TYPE_CHECKING:
- from pynecone.state import State
- class Var(ABC):
- """An abstract var."""
- # The name of the var.
- name: str
- # The type of the var.
- type_: Type
- # The name of the enclosing state.
- state: str = ""
- # Whether this is a local javascript variable.
- is_local: bool = False
- # Whether the var is a string literal.
- is_string: bool = False
- @classmethod
- def create(
- cls, value: Any, is_local: bool = True, is_string: bool = False
- ) -> Optional[Var]:
- """Create a var from a value.
- Args:
- value: The value to create the var from.
- is_local: Whether the var is local.
- is_string: Whether the var is a string literal.
- Returns:
- The var.
- """
- # Check for none values.
- if value is None:
- return None
- # If the value is already a var, do nothing.
- if isinstance(value, Var):
- return value
- type_ = type(value)
- # Special case for plotly figures.
- if isinstance(value, Figure):
- value = json.loads(to_json(value))["data"]
- type_ = Figure
- name = json.dumps(value) if not isinstance(value, str) else value
- return BaseVar(name=name, type_=type_, is_local=is_local, is_string=is_string)
- @classmethod
- def __class_getitem__(cls, type_: str) -> _GenericAlias:
- """Get a typed var.
- Args:
- type_: The type of the var.
- Returns:
- The var class item.
- """
- return _GenericAlias(cls, type_)
- def equals(self, other: Var) -> bool:
- """Check if two vars are equal.
- Args:
- other: The other var to compare.
- Returns:
- Whether the vars are equal.
- """
- return (
- self.name == other.name
- and self.type_ == other.type_
- and self.state == other.state
- and self.is_local == other.is_local
- )
- def to_string(self) -> Var:
- """Convert a var to a string.
- Returns:
- The stringified var.
- """
- return self.operation(fn="JSON.stringify")
- def __hash__(self) -> int:
- """Define a hash function for a var.
- Returns:
- The hash of the var.
- """
- return hash((self.name, str(self.type_)))
- def __str__(self) -> str:
- """Wrap the var so it can be used in templates.
- Returns:
- The wrapped var, i.e. {state.var}.
- """
- if self.is_local:
- out = self.full_name
- else:
- out = utils.wrap(self.full_name, "{")
- if self.is_string:
- out = utils.format_string(out)
- return out
- def __getitem__(self, i: Any) -> Var:
- """Index into a var.
- Args:
- i: The index to index into.
- Returns:
- The indexed var.
- Raises:
- TypeError: If the var is not indexable.
- """
- # The type of the indexed var.
- type_ = str
- # Convert any vars to local vars.
- if isinstance(i, Var):
- i = BaseVar(name=i.name, type_=i.type_, state=i.state, is_local=True)
- if utils._issubclass(self.type_, List):
- assert isinstance(
- i, utils.get_args(Union[int, Var])
- ), "Index must be an integer."
- if utils.is_generic_alias(self.type_):
- type_ = utils.get_args(self.type_)[0]
- else:
- type_ = Any
- elif utils._issubclass(self.type_, Dict) or utils.is_dataframe(self.type_):
- if isinstance(i, str):
- i = utils.wrap(i, '"')
- if utils.is_generic_alias(self.type_):
- type_ = utils.get_args(self.type_)[1]
- else:
- type_ = Any
- else:
- raise TypeError(
- f"Var {self.name} of type {self.type_} does not support indexing."
- )
- return BaseVar(
- name=f"{self.name}[{i}]",
- type_=type_,
- state=self.state,
- )
- def __getattribute__(self, name: str) -> Var:
- """Get a var attribute.
- Args:
- name: The name of the attribute.
- Returns:
- The var attribute.
- Raises:
- Exception: If the attribute is not found.
- """
- try:
- return super().__getattribute__(name)
- except Exception as e:
- # Check if the attribute is one of the class fields.
- if (
- not name.startswith("_")
- and hasattr(self.type_, "__fields__")
- and name in self.type_.__fields__
- ):
- type_ = self.type_.__fields__[name].outer_type_
- if isinstance(type_, ModelField):
- type_ = type_.type_
- return BaseVar(
- name=f"{self.name}.{name}",
- type_=type_,
- state=self.state,
- )
- raise e
- def operation(
- self,
- op: str = "",
- other: Optional[Var] = None,
- type_: Optional[Type] = None,
- flip: bool = False,
- fn: Optional[str] = None,
- ) -> Var:
- """Perform an operation on a var.
- Args:
- op: The operation to perform.
- other: The other var to perform the operation on.
- type_: The type of the operation result.
- flip: Whether to flip the order of the operation.
- fn: A function to apply to the operation.
- Returns:
- The operation result.
- """
- # Wrap strings in quotes.
- if isinstance(other, str):
- other = Var.create(json.dumps(other))
- else:
- other = Var.create(other)
- if type_ is None:
- type_ = self.type_
- if other is None:
- name = f"{op}{self.full_name}"
- else:
- props = (self, other) if not flip else (other, self)
- name = f"{props[0].full_name} {op} {props[1].full_name}"
- if fn is None:
- name = utils.wrap(name, "(")
- if fn is not None:
- name = f"{fn}({name})"
- return BaseVar(
- name=name,
- type_=type_,
- )
- def compare(self, op: str, other: Var) -> Var:
- """Compare two vars with inequalities.
- Args:
- op: The comparison operator.
- other: The other var to compare with.
- Returns:
- The comparison result.
- """
- return self.operation(op, other, bool)
- def __invert__(self) -> Var:
- """Invert a var.
- Returns:
- The inverted var.
- """
- return self.operation("!", type_=bool)
- def __neg__(self) -> Var:
- """Negate a var.
- Returns:
- The negated var.
- """
- return self.operation(fn="-")
- def __abs__(self) -> Var:
- """Get the absolute value of a var.
- Returns:
- A var with the absolute value.
- """
- return self.operation(fn="Math.abs")
- def __eq__(self, other: Var) -> Var:
- """Perform an equality comparison.
- Args:
- other: The other var to compare with.
- Returns:
- A var representing the equality comparison.
- """
- return self.compare("==", other)
- def __ne__(self, other: Var) -> Var:
- """Perform an inequality comparison.
- Args:
- other: The other var to compare with.
- Returns:
- A var representing the inequality comparison.
- """
- return self.compare("!=", other)
- def __gt__(self, other: Var) -> Var:
- """Perform a greater than comparison.
- Args:
- other: The other var to compare with.
- Returns:
- A var representing the greater than comparison.
- """
- return self.compare(">", other)
- def __ge__(self, other: Var) -> Var:
- """Perform a greater than or equal to comparison.
- Args:
- other: The other var to compare with.
- Returns:
- A var representing the greater than or equal to comparison.
- """
- return self.compare(">=", other)
- def __lt__(self, other: Var) -> Var:
- """Perform a less than comparison.
- Args:
- other: The other var to compare with.
- Returns:
- A var representing the less than comparison.
- """
- return self.compare("<", other)
- def __le__(self, other: Var) -> Var:
- """Perform a less than or equal to comparison.
- Args:
- other: The other var to compare with.
- Returns:
- A var representing the less than or equal to comparison.
- """
- return self.compare("<=", other)
- def __add__(self, other: Var) -> Var:
- """Add two vars.
- Args:
- other: The other var to add.
- Returns:
- A var representing the sum.
- """
- return self.operation("+", other)
- def __radd__(self, other: Var) -> Var:
- """Add two vars.
- Args:
- other: The other var to add.
- Returns:
- A var representing the sum.
- """
- return self.operation("+", other, flip=True)
- def __sub__(self, other: Var) -> Var:
- """Subtract two vars.
- Args:
- other: The other var to subtract.
- Returns:
- A var representing the difference.
- """
- return self.operation("-", other)
- def __rsub__(self, other: Var) -> Var:
- """Subtract two vars.
- Args:
- other: The other var to subtract.
- Returns:
- A var representing the difference.
- """
- return self.operation("-", other, flip=True)
- def __mul__(self, other: Var) -> Var:
- """Multiply two vars.
- Args:
- other: The other var to multiply.
- Returns:
- A var representing the product.
- """
- return self.operation("*", other)
- def __rmul__(self, other: Var) -> Var:
- """Multiply two vars.
- Args:
- other: The other var to multiply.
- Returns:
- A var representing the product.
- """
- return self.operation("*", other, flip=True)
- def __pow__(self, other: Var) -> Var:
- """Raise a var to a power.
- Args:
- other: The power to raise to.
- Returns:
- A var representing the power.
- """
- return self.operation(",", other, fn="Math.pow")
- def __rpow__(self, other: Var) -> Var:
- """Raise a var to a power.
- Args:
- other: The power to raise to.
- Returns:
- A var representing the power.
- """
- return self.operation(",", other, flip=True, fn="Math.pow")
- def __truediv__(self, other: Var) -> Var:
- """Divide two vars.
- Args:
- other: The other var to divide.
- Returns:
- A var representing the quotient.
- """
- return self.operation("/", other)
- def __rtruediv__(self, other: Var) -> Var:
- """Divide two vars.
- Args:
- other: The other var to divide.
- Returns:
- A var representing the quotient.
- """
- return self.operation("/", other, flip=True)
- def __floordiv__(self, other: Var) -> Var:
- """Divide two vars.
- Args:
- other: The other var to divide.
- Returns:
- A var representing the quotient.
- """
- return self.operation("/", other, fn="Math.floor")
- def __mod__(self, other: Var) -> Var:
- """Get the remainder of two vars.
- Args:
- other: The other var to divide.
- Returns:
- A var representing the remainder.
- """
- return self.operation("%", other)
- def __rmod__(self, other: Var) -> Var:
- """Get the remainder of two vars.
- Args:
- other: The other var to divide.
- Returns:
- A var representing the remainder.
- """
- return self.operation("%", other, flip=True)
- def __and__(self, other: Var) -> Var:
- """Perform a logical and.
- Args:
- other: The other var to perform the logical and with.
- Returns:
- A var representing the logical and.
- """
- return self.operation("&&", other)
- def __rand__(self, other: Var) -> Var:
- """Perform a logical and.
- Args:
- other: The other var to perform the logical and with.
- Returns:
- A var representing the logical and.
- """
- return self.operation("&&", other, flip=True)
- def __or__(self, other: Var) -> Var:
- """Perform a logical or.
- Args:
- other: The other var to perform the logical or with.
- Returns:
- A var representing the logical or.
- """
- return self.operation("||", other)
- def __ror__(self, other: Var) -> Var:
- """Perform a logical or.
- Args:
- other: The other var to perform the logical or with.
- Returns:
- A var representing the logical or.
- """
- return self.operation("||", other, flip=True)
- def foreach(self, fn: Callable) -> Var:
- """Return a list of components. after doing a foreach on this var.
- Args:
- fn: The function to call on each component.
- Returns:
- A var representing foreach operation.
- """
- arg = BaseVar(
- name=utils.get_unique_variable_name(),
- type_=self.type_,
- )
- return BaseVar(
- name=f"{self.full_name}.map(({arg.name}, i) => {fn(arg, key='i')})",
- type_=self.type_,
- )
- def to(self, type_: Type) -> Var:
- """Convert the type of the var.
- Args:
- type_: The type to convert to.
- Returns:
- The converted var.
- """
- return BaseVar(
- name=self.name,
- type_=type_,
- state=self.state,
- is_local=self.is_local,
- )
- @property
- def full_name(self) -> str:
- """Get the full name of the var.
- Returns:
- The full name of the var.
- """
- if self.state == "":
- return self.name
- return ".".join([self.state, self.name])
- def set_state(self, state: Type[State]) -> Any:
- """Set the state of the var.
- Args:
- state: The state to set.
- Returns:
- The var with the set state.
- """
- self.state = state.get_full_name()
- return self
- class BaseVar(Var, Base):
- """A base (non-computed) var of the app state."""
- # The name of the var.
- name: str
- # The type of the var.
- type_: Any
- # The name of the enclosing state.
- state: str = ""
- # Whether this is a local javascript variable.
- is_local: bool = False
- is_string: bool = False
- def __hash__(self) -> int:
- """Define a hash function for a var.
- Returns:
- The hash of the var.
- """
- return hash((self.name, str(self.type_)))
- def get_default_value(self) -> Any:
- """Get the default value of the var.
- Returns:
- The default value of the var.
- """
- if utils.is_generic_alias(self.type_):
- type_ = self.type_.__origin__
- else:
- type_ = self.type_
- if issubclass(type_, str):
- return ""
- if issubclass(type_, utils.get_args(Union[int, float])):
- return 0
- if issubclass(type_, bool):
- return False
- if issubclass(type_, list):
- return []
- if issubclass(type_, dict):
- return {}
- if issubclass(type_, tuple):
- return ()
- if issubclass(type_, set):
- return set()
- return None
- def get_setter_name(self, include_state: bool = True) -> str:
- """Get the name of the var's generated setter function.
- Args:
- include_state: Whether to include the state name in the setter name.
- Returns:
- The name of the setter function.
- """
- setter = constants.SETTER_PREFIX + self.name
- if not include_state or self.state == "":
- return setter
- return ".".join((self.state, setter))
- def get_setter(self) -> Callable[[State, Any], None]:
- """Get the var's setter function.
- Returns:
- A function that that creates a setter for the var.
- """
- def setter(state: State, value: Any):
- """Get the setter for the var.
- Args:
- state: The state within which we add the setter function.
- value: The value to set.
- """
- setattr(state, self.name, value)
- setter.__qualname__ = self.get_setter_name()
- return setter
- def json(self) -> str:
- """Convert the object to a json string.
- Returns:
- The object as a json string.
- """
- return self.__config__.json_dumps(self.dict())
- class ComputedVar(property, Var):
- """A field with computed getters."""
- @property
- def name(self) -> str:
- """Get the name of the var.
- Returns:
- The name of the var.
- """
- assert self.fget is not None, "Var must have a getter."
- return self.fget.__name__
- @property
- def type_(self):
- """Get the type of the var.
- Returns:
- The type of the var.
- """
- if "return" in self.fget.__annotations__:
- return self.fget.__annotations__["return"]
- return Any
|