var.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725
  1. """Define a state var."""
  2. from __future__ import annotations
  3. import json
  4. from abc import ABC
  5. from typing import _GenericAlias # type: ignore
  6. from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Type, Union
  7. from plotly.graph_objects import Figure
  8. from plotly.io import to_json
  9. from pydantic.fields import ModelField
  10. from pynecone import constants, utils
  11. from pynecone.base import Base
  12. if TYPE_CHECKING:
  13. from pynecone.state import State
  14. class Var(ABC):
  15. """An abstract var."""
  16. # The name of the var.
  17. name: str
  18. # The type of the var.
  19. type_: Type
  20. # The name of the enclosing state.
  21. state: str = ""
  22. # Whether this is a local javascript variable.
  23. is_local: bool = False
  24. # Whether the var is a string literal.
  25. is_string: bool = False
  26. @classmethod
  27. def create(
  28. cls, value: Any, is_local: bool = True, is_string: bool = False
  29. ) -> Optional[Var]:
  30. """Create a var from a value.
  31. Args:
  32. value: The value to create the var from.
  33. is_local: Whether the var is local.
  34. is_string: Whether the var is a string literal.
  35. Returns:
  36. The var.
  37. """
  38. # Check for none values.
  39. if value is None:
  40. return None
  41. # If the value is already a var, do nothing.
  42. if isinstance(value, Var):
  43. return value
  44. type_ = type(value)
  45. # Special case for plotly figures.
  46. if isinstance(value, Figure):
  47. value = json.loads(to_json(value))["data"]
  48. type_ = Figure
  49. name = json.dumps(value) if not isinstance(value, str) else value
  50. return BaseVar(name=name, type_=type_, is_local=is_local, is_string=is_string)
  51. @classmethod
  52. def __class_getitem__(cls, type_: str) -> _GenericAlias:
  53. """Get a typed var.
  54. Args:
  55. type_: The type of the var.
  56. Returns:
  57. The var class item.
  58. """
  59. return _GenericAlias(cls, type_)
  60. def equals(self, other: Var) -> bool:
  61. """Check if two vars are equal.
  62. Args:
  63. other: The other var to compare.
  64. Returns:
  65. Whether the vars are equal.
  66. """
  67. return (
  68. self.name == other.name
  69. and self.type_ == other.type_
  70. and self.state == other.state
  71. and self.is_local == other.is_local
  72. )
  73. def to_string(self) -> Var:
  74. """Convert a var to a string.
  75. Returns:
  76. The stringified var.
  77. """
  78. return self.operation(fn="JSON.stringify")
  79. def __hash__(self) -> int:
  80. """Define a hash function for a var.
  81. Returns:
  82. The hash of the var.
  83. """
  84. return hash((self.name, str(self.type_)))
  85. def __str__(self) -> str:
  86. """Wrap the var so it can be used in templates.
  87. Returns:
  88. The wrapped var, i.e. {state.var}.
  89. """
  90. if self.is_local:
  91. out = self.full_name
  92. else:
  93. out = utils.wrap(self.full_name, "{")
  94. if self.is_string:
  95. out = utils.format_string(out)
  96. return out
  97. def __getitem__(self, i: Any) -> Var:
  98. """Index into a var.
  99. Args:
  100. i: The index to index into.
  101. Returns:
  102. The indexed var.
  103. Raises:
  104. TypeError: If the var is not indexable.
  105. """
  106. # The type of the indexed var.
  107. type_ = str
  108. # Convert any vars to local vars.
  109. if isinstance(i, Var):
  110. i = BaseVar(name=i.name, type_=i.type_, state=i.state, is_local=True)
  111. if utils._issubclass(self.type_, List):
  112. assert isinstance(
  113. i, utils.get_args(Union[int, Var])
  114. ), "Index must be an integer."
  115. if utils.is_generic_alias(self.type_):
  116. type_ = utils.get_args(self.type_)[0]
  117. else:
  118. type_ = Any
  119. elif utils._issubclass(self.type_, Dict) or utils.is_dataframe(self.type_):
  120. if isinstance(i, str):
  121. i = utils.wrap(i, '"')
  122. if utils.is_generic_alias(self.type_):
  123. type_ = utils.get_args(self.type_)[1]
  124. else:
  125. type_ = Any
  126. else:
  127. raise TypeError(
  128. f"Var {self.name} of type {self.type_} does not support indexing."
  129. )
  130. return BaseVar(
  131. name=f"{self.name}[{i}]",
  132. type_=type_,
  133. state=self.state,
  134. )
  135. def __getattribute__(self, name: str) -> Var:
  136. """Get a var attribute.
  137. Args:
  138. name: The name of the attribute.
  139. Returns:
  140. The var attribute.
  141. Raises:
  142. Exception: If the attribute is not found.
  143. """
  144. try:
  145. return super().__getattribute__(name)
  146. except Exception as e:
  147. # Check if the attribute is one of the class fields.
  148. if (
  149. not name.startswith("_")
  150. and hasattr(self.type_, "__fields__")
  151. and name in self.type_.__fields__
  152. ):
  153. type_ = self.type_.__fields__[name].outer_type_
  154. if isinstance(type_, ModelField):
  155. type_ = type_.type_
  156. return BaseVar(
  157. name=f"{self.name}.{name}",
  158. type_=type_,
  159. state=self.state,
  160. )
  161. raise e
  162. def operation(
  163. self,
  164. op: str = "",
  165. other: Optional[Var] = None,
  166. type_: Optional[Type] = None,
  167. flip: bool = False,
  168. fn: Optional[str] = None,
  169. ) -> Var:
  170. """Perform an operation on a var.
  171. Args:
  172. op: The operation to perform.
  173. other: The other var to perform the operation on.
  174. type_: The type of the operation result.
  175. flip: Whether to flip the order of the operation.
  176. fn: A function to apply to the operation.
  177. Returns:
  178. The operation result.
  179. """
  180. # Wrap strings in quotes.
  181. if isinstance(other, str):
  182. other = Var.create(json.dumps(other))
  183. else:
  184. other = Var.create(other)
  185. if type_ is None:
  186. type_ = self.type_
  187. if other is None:
  188. name = f"{op}{self.full_name}"
  189. else:
  190. props = (self, other) if not flip else (other, self)
  191. name = f"{props[0].full_name} {op} {props[1].full_name}"
  192. if fn is None:
  193. name = utils.wrap(name, "(")
  194. if fn is not None:
  195. name = f"{fn}({name})"
  196. return BaseVar(
  197. name=name,
  198. type_=type_,
  199. )
  200. def compare(self, op: str, other: Var) -> Var:
  201. """Compare two vars with inequalities.
  202. Args:
  203. op: The comparison operator.
  204. other: The other var to compare with.
  205. Returns:
  206. The comparison result.
  207. """
  208. return self.operation(op, other, bool)
  209. def __invert__(self) -> Var:
  210. """Invert a var.
  211. Returns:
  212. The inverted var.
  213. """
  214. return self.operation("!", type_=bool)
  215. def __neg__(self) -> Var:
  216. """Negate a var.
  217. Returns:
  218. The negated var.
  219. """
  220. return self.operation(fn="-")
  221. def __abs__(self) -> Var:
  222. """Get the absolute value of a var.
  223. Returns:
  224. A var with the absolute value.
  225. """
  226. return self.operation(fn="Math.abs")
  227. def length(self) -> Var:
  228. """Get the length of a list var.
  229. Returns:
  230. A var with the absolute value.
  231. Raises:
  232. ValueError: If the var is not a list.
  233. """
  234. if not utils._issubclass(self.type_, List):
  235. raise ValueError(f"Cannot get length of non-list var {self}.")
  236. return BaseVar(
  237. name=f"{self.full_name}.length",
  238. type_=int,
  239. )
  240. def __eq__(self, other: Var) -> Var:
  241. """Perform an equality comparison.
  242. Args:
  243. other: The other var to compare with.
  244. Returns:
  245. A var representing the equality comparison.
  246. """
  247. return self.compare("==", other)
  248. def __ne__(self, other: Var) -> Var:
  249. """Perform an inequality comparison.
  250. Args:
  251. other: The other var to compare with.
  252. Returns:
  253. A var representing the inequality comparison.
  254. """
  255. return self.compare("!=", other)
  256. def __gt__(self, other: Var) -> Var:
  257. """Perform a greater than comparison.
  258. Args:
  259. other: The other var to compare with.
  260. Returns:
  261. A var representing the greater than comparison.
  262. """
  263. return self.compare(">", other)
  264. def __ge__(self, other: Var) -> Var:
  265. """Perform a greater than or equal to comparison.
  266. Args:
  267. other: The other var to compare with.
  268. Returns:
  269. A var representing the greater than or equal to comparison.
  270. """
  271. return self.compare(">=", other)
  272. def __lt__(self, other: Var) -> Var:
  273. """Perform a less than comparison.
  274. Args:
  275. other: The other var to compare with.
  276. Returns:
  277. A var representing the less than comparison.
  278. """
  279. return self.compare("<", other)
  280. def __le__(self, other: Var) -> Var:
  281. """Perform a less than or equal to comparison.
  282. Args:
  283. other: The other var to compare with.
  284. Returns:
  285. A var representing the less than or equal to comparison.
  286. """
  287. return self.compare("<=", other)
  288. def __add__(self, other: Var) -> Var:
  289. """Add two vars.
  290. Args:
  291. other: The other var to add.
  292. Returns:
  293. A var representing the sum.
  294. """
  295. return self.operation("+", other)
  296. def __radd__(self, other: Var) -> Var:
  297. """Add two vars.
  298. Args:
  299. other: The other var to add.
  300. Returns:
  301. A var representing the sum.
  302. """
  303. return self.operation("+", other, flip=True)
  304. def __sub__(self, other: Var) -> Var:
  305. """Subtract two vars.
  306. Args:
  307. other: The other var to subtract.
  308. Returns:
  309. A var representing the difference.
  310. """
  311. return self.operation("-", other)
  312. def __rsub__(self, other: Var) -> Var:
  313. """Subtract two vars.
  314. Args:
  315. other: The other var to subtract.
  316. Returns:
  317. A var representing the difference.
  318. """
  319. return self.operation("-", other, flip=True)
  320. def __mul__(self, other: Var) -> Var:
  321. """Multiply two vars.
  322. Args:
  323. other: The other var to multiply.
  324. Returns:
  325. A var representing the product.
  326. """
  327. return self.operation("*", other)
  328. def __rmul__(self, other: Var) -> Var:
  329. """Multiply two vars.
  330. Args:
  331. other: The other var to multiply.
  332. Returns:
  333. A var representing the product.
  334. """
  335. return self.operation("*", other, flip=True)
  336. def __pow__(self, other: Var) -> Var:
  337. """Raise a var to a power.
  338. Args:
  339. other: The power to raise to.
  340. Returns:
  341. A var representing the power.
  342. """
  343. return self.operation(",", other, fn="Math.pow")
  344. def __rpow__(self, other: Var) -> Var:
  345. """Raise a var to a power.
  346. Args:
  347. other: The power to raise to.
  348. Returns:
  349. A var representing the power.
  350. """
  351. return self.operation(",", other, flip=True, fn="Math.pow")
  352. def __truediv__(self, other: Var) -> Var:
  353. """Divide two vars.
  354. Args:
  355. other: The other var to divide.
  356. Returns:
  357. A var representing the quotient.
  358. """
  359. return self.operation("/", other)
  360. def __rtruediv__(self, other: Var) -> Var:
  361. """Divide two vars.
  362. Args:
  363. other: The other var to divide.
  364. Returns:
  365. A var representing the quotient.
  366. """
  367. return self.operation("/", other, flip=True)
  368. def __floordiv__(self, other: Var) -> Var:
  369. """Divide two vars.
  370. Args:
  371. other: The other var to divide.
  372. Returns:
  373. A var representing the quotient.
  374. """
  375. return self.operation("/", other, fn="Math.floor")
  376. def __mod__(self, other: Var) -> Var:
  377. """Get the remainder of two vars.
  378. Args:
  379. other: The other var to divide.
  380. Returns:
  381. A var representing the remainder.
  382. """
  383. return self.operation("%", other)
  384. def __rmod__(self, other: Var) -> Var:
  385. """Get the remainder of two vars.
  386. Args:
  387. other: The other var to divide.
  388. Returns:
  389. A var representing the remainder.
  390. """
  391. return self.operation("%", other, flip=True)
  392. def __and__(self, other: Var) -> Var:
  393. """Perform a logical and.
  394. Args:
  395. other: The other var to perform the logical and with.
  396. Returns:
  397. A var representing the logical and.
  398. """
  399. return self.operation("&&", other)
  400. def __rand__(self, other: Var) -> Var:
  401. """Perform a logical and.
  402. Args:
  403. other: The other var to perform the logical and with.
  404. Returns:
  405. A var representing the logical and.
  406. """
  407. return self.operation("&&", other, flip=True)
  408. def __or__(self, other: Var) -> Var:
  409. """Perform a logical or.
  410. Args:
  411. other: The other var to perform the logical or with.
  412. Returns:
  413. A var representing the logical or.
  414. """
  415. return self.operation("||", other)
  416. def __ror__(self, other: Var) -> Var:
  417. """Perform a logical or.
  418. Args:
  419. other: The other var to perform the logical or with.
  420. Returns:
  421. A var representing the logical or.
  422. """
  423. return self.operation("||", other, flip=True)
  424. def foreach(self, fn: Callable) -> Var:
  425. """Return a list of components. after doing a foreach on this var.
  426. Args:
  427. fn: The function to call on each component.
  428. Returns:
  429. A var representing foreach operation.
  430. """
  431. arg = BaseVar(
  432. name=utils.get_unique_variable_name(),
  433. type_=self.type_,
  434. )
  435. return BaseVar(
  436. name=f"{self.full_name}.map(({arg.name}, i) => {fn(arg, key='i')})",
  437. type_=self.type_,
  438. )
  439. def to(self, type_: Type) -> Var:
  440. """Convert the type of the var.
  441. Args:
  442. type_: The type to convert to.
  443. Returns:
  444. The converted var.
  445. """
  446. return BaseVar(
  447. name=self.name,
  448. type_=type_,
  449. state=self.state,
  450. is_local=self.is_local,
  451. )
  452. @property
  453. def full_name(self) -> str:
  454. """Get the full name of the var.
  455. Returns:
  456. The full name of the var.
  457. """
  458. if self.state == "":
  459. return self.name
  460. return ".".join([self.state, self.name])
  461. def set_state(self, state: Type[State]) -> Any:
  462. """Set the state of the var.
  463. Args:
  464. state: The state to set.
  465. Returns:
  466. The var with the set state.
  467. """
  468. self.state = state.get_full_name()
  469. return self
  470. class BaseVar(Var, Base):
  471. """A base (non-computed) var of the app state."""
  472. # The name of the var.
  473. name: str
  474. # The type of the var.
  475. type_: Any
  476. # The name of the enclosing state.
  477. state: str = ""
  478. # Whether this is a local javascript variable.
  479. is_local: bool = False
  480. is_string: bool = False
  481. def __hash__(self) -> int:
  482. """Define a hash function for a var.
  483. Returns:
  484. The hash of the var.
  485. """
  486. return hash((self.name, str(self.type_)))
  487. def get_default_value(self) -> Any:
  488. """Get the default value of the var.
  489. Returns:
  490. The default value of the var.
  491. """
  492. if utils.is_generic_alias(self.type_):
  493. type_ = self.type_.__origin__
  494. else:
  495. type_ = self.type_
  496. if issubclass(type_, str):
  497. return ""
  498. if issubclass(type_, utils.get_args(Union[int, float])):
  499. return 0
  500. if issubclass(type_, bool):
  501. return False
  502. if issubclass(type_, list):
  503. return []
  504. if issubclass(type_, dict):
  505. return {}
  506. if issubclass(type_, tuple):
  507. return ()
  508. if issubclass(type_, set):
  509. return set()
  510. return None
  511. def get_setter_name(self, include_state: bool = True) -> str:
  512. """Get the name of the var's generated setter function.
  513. Args:
  514. include_state: Whether to include the state name in the setter name.
  515. Returns:
  516. The name of the setter function.
  517. """
  518. setter = constants.SETTER_PREFIX + self.name
  519. if not include_state or self.state == "":
  520. return setter
  521. return ".".join((self.state, setter))
  522. def get_setter(self) -> Callable[[State, Any], None]:
  523. """Get the var's setter function.
  524. Returns:
  525. A function that that creates a setter for the var.
  526. """
  527. def setter(state: State, value: Any):
  528. """Get the setter for the var.
  529. Args:
  530. state: The state within which we add the setter function.
  531. value: The value to set.
  532. """
  533. setattr(state, self.name, value)
  534. setter.__qualname__ = self.get_setter_name()
  535. return setter
  536. def json(self) -> str:
  537. """Convert the object to a json string.
  538. Returns:
  539. The object as a json string.
  540. """
  541. return self.__config__.json_dumps(self.dict())
  542. class ComputedVar(property, Var):
  543. """A field with computed getters."""
  544. @property
  545. def name(self) -> str:
  546. """Get the name of the var.
  547. Returns:
  548. The name of the var.
  549. """
  550. assert self.fget is not None, "Var must have a getter."
  551. return self.fget.__name__
  552. @property
  553. def type_(self):
  554. """Get the type of the var.
  555. Returns:
  556. The type of the var.
  557. """
  558. if "return" in self.fget.__annotations__:
  559. return self.fget.__annotations__["return"]
  560. return Any