var.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988
  1. """Define a state var."""
  2. from __future__ import annotations
  3. import json
  4. import random
  5. import string
  6. from abc import ABC
  7. from typing import (
  8. TYPE_CHECKING,
  9. Any,
  10. Callable,
  11. Dict,
  12. List,
  13. Optional,
  14. Type,
  15. Union,
  16. _GenericAlias, # type: ignore
  17. )
  18. from plotly.graph_objects import Figure
  19. from plotly.io import to_json
  20. from pydantic.fields import ModelField
  21. from pynecone import constants
  22. from pynecone.base import Base
  23. from pynecone.utils import format, types
  24. if TYPE_CHECKING:
  25. from pynecone.state import State
  26. # Set of unique variable names.
  27. USED_VARIABLES = set()
  28. def get_unique_variable_name() -> str:
  29. """Get a unique variable name.
  30. Returns:
  31. The unique variable name.
  32. """
  33. name = "".join([random.choice(string.ascii_lowercase) for _ in range(8)])
  34. if name not in USED_VARIABLES:
  35. USED_VARIABLES.add(name)
  36. return name
  37. return get_unique_variable_name()
  38. class Var(ABC):
  39. """An abstract var."""
  40. # The name of the var.
  41. name: str
  42. # The type of the var.
  43. type_: Type
  44. # The name of the enclosing state.
  45. state: str = ""
  46. # Whether this is a local javascript variable.
  47. is_local: bool = False
  48. # Whether the var is a string literal.
  49. is_string: bool = False
  50. @classmethod
  51. def create(
  52. cls, value: Any, is_local: bool = True, is_string: bool = False
  53. ) -> Optional[Var]:
  54. """Create a var from a value.
  55. Args:
  56. value: The value to create the var from.
  57. is_local: Whether the var is local.
  58. is_string: Whether the var is a string literal.
  59. Returns:
  60. The var.
  61. Raises:
  62. TypeError: If the value is JSON-unserializable.
  63. """
  64. # Check for none values.
  65. if value is None:
  66. return None
  67. # If the value is already a var, do nothing.
  68. if isinstance(value, Var):
  69. return value
  70. type_ = type(value)
  71. # Special case for plotly figures.
  72. if isinstance(value, Figure):
  73. value = json.loads(to_json(value))["data"] # type: ignore
  74. type_ = Figure
  75. try:
  76. name = value if isinstance(value, str) else json.dumps(value)
  77. except TypeError as e:
  78. raise TypeError(
  79. f"To create a Var must be Var or JSON-serializable. Got {value} of type {type(value)}."
  80. ) from e
  81. return BaseVar(name=name, type_=type_, is_local=is_local, is_string=is_string)
  82. @classmethod
  83. def create_safe(
  84. cls, value: Any, is_local: bool = True, is_string: bool = False
  85. ) -> Var:
  86. """Create a var from a value, guaranteeing that it is not None.
  87. Args:
  88. value: The value to create the var from.
  89. is_local: Whether the var is local.
  90. is_string: Whether the var is a string literal.
  91. Returns:
  92. The var.
  93. """
  94. var = cls.create(value, is_local=is_local, is_string=is_string)
  95. assert var is not None
  96. return var
  97. @classmethod
  98. def __class_getitem__(cls, type_: str) -> _GenericAlias:
  99. """Get a typed var.
  100. Args:
  101. type_: The type of the var.
  102. Returns:
  103. The var class item.
  104. """
  105. return _GenericAlias(cls, type_)
  106. def equals(self, other: Var) -> bool:
  107. """Check if two vars are equal.
  108. Args:
  109. other: The other var to compare.
  110. Returns:
  111. Whether the vars are equal.
  112. """
  113. return (
  114. self.name == other.name
  115. and self.type_ == other.type_
  116. and self.state == other.state
  117. and self.is_local == other.is_local
  118. )
  119. def to_string(self) -> Var:
  120. """Convert a var to a string.
  121. Returns:
  122. The stringified var.
  123. """
  124. return self.operation(fn="JSON.stringify")
  125. def __hash__(self) -> int:
  126. """Define a hash function for a var.
  127. Returns:
  128. The hash of the var.
  129. """
  130. return hash((self.name, str(self.type_)))
  131. def __str__(self) -> str:
  132. """Wrap the var so it can be used in templates.
  133. Returns:
  134. The wrapped var, i.e. {state.var}.
  135. """
  136. out = self.full_name if self.is_local else format.wrap(self.full_name, "{")
  137. if self.is_string:
  138. out = format.format_string(out)
  139. return out
  140. def __getitem__(self, i: Any) -> Var:
  141. """Index into a var.
  142. Args:
  143. i: The index to index into.
  144. Returns:
  145. The indexed var.
  146. Raises:
  147. TypeError: If the var is not indexable.
  148. """
  149. # Indexing is only supported for lists, dicts, and dataframes.
  150. if not (
  151. types._issubclass(self.type_, Union[List, Dict])
  152. or types.is_dataframe(self.type_)
  153. ):
  154. if self.type_ == Any:
  155. raise TypeError(
  156. f"Could not index into var of type Any. (If you are trying to index into a state var, add a type annotation to the var.)"
  157. )
  158. raise TypeError(
  159. f"Var {self.name} of type {self.type_} does not support indexing."
  160. )
  161. # The type of the indexed var.
  162. type_ = Any
  163. # Convert any vars to local vars.
  164. if isinstance(i, Var):
  165. i = BaseVar(name=i.name, type_=i.type_, state=i.state, is_local=True)
  166. # Handle list indexing.
  167. if types._issubclass(self.type_, List):
  168. # List indices must be ints, slices, or vars.
  169. if not isinstance(i, types.get_args(Union[int, slice, Var])):
  170. raise TypeError("Index must be an integer.")
  171. # Handle slices first.
  172. if isinstance(i, slice):
  173. # Get the start and stop indices.
  174. start = i.start or 0
  175. stop = i.stop or "undefined"
  176. # Use the slice function.
  177. return BaseVar(
  178. name=f"{self.name}.slice({start}, {stop})",
  179. type_=self.type_,
  180. state=self.state,
  181. )
  182. # Get the type of the indexed var.
  183. type_ = (
  184. types.get_args(self.type_)[0]
  185. if types.is_generic_alias(self.type_)
  186. else Any
  187. )
  188. # Use `at` to support negative indices.
  189. return BaseVar(
  190. name=f"{self.name}.at({i})",
  191. type_=type_,
  192. state=self.state,
  193. )
  194. # Dictionary / dataframe indexing.
  195. # Get the type of the indexed var.
  196. if isinstance(i, str):
  197. i = format.wrap(i, '"')
  198. type_ = (
  199. types.get_args(self.type_)[1] if types.is_generic_alias(self.type_) else Any
  200. )
  201. # Use normal indexing here.
  202. return BaseVar(
  203. name=f"{self.name}[{i}]",
  204. type_=type_,
  205. state=self.state,
  206. )
  207. def __getattribute__(self, name: str) -> Var:
  208. """Get a var attribute.
  209. Args:
  210. name: The name of the attribute.
  211. Returns:
  212. The var attribute.
  213. Raises:
  214. Exception: If the attribute is not found.
  215. """
  216. try:
  217. return super().__getattribute__(name)
  218. except Exception as e:
  219. # Check if the attribute is one of the class fields.
  220. if (
  221. not name.startswith("_")
  222. and hasattr(self.type_, "__fields__")
  223. and name in self.type_.__fields__
  224. ):
  225. type_ = self.type_.__fields__[name].outer_type_
  226. if isinstance(type_, ModelField):
  227. type_ = type_.type_
  228. return BaseVar(
  229. name=f"{self.name}.{name}",
  230. type_=type_,
  231. state=self.state,
  232. )
  233. raise e
  234. def operation(
  235. self,
  236. op: str = "",
  237. other: Optional[Var] = None,
  238. type_: Optional[Type] = None,
  239. flip: bool = False,
  240. fn: Optional[str] = None,
  241. ) -> Var:
  242. """Perform an operation on a var.
  243. Args:
  244. op: The operation to perform.
  245. other: The other var to perform the operation on.
  246. type_: The type of the operation result.
  247. flip: Whether to flip the order of the operation.
  248. fn: A function to apply to the operation.
  249. Returns:
  250. The operation result.
  251. """
  252. # Wrap strings in quotes.
  253. if isinstance(other, str):
  254. other = Var.create(json.dumps(other))
  255. else:
  256. other = Var.create(other)
  257. if type_ is None:
  258. type_ = self.type_
  259. if other is None:
  260. name = f"{op}{self.full_name}"
  261. else:
  262. props = (other, self) if flip else (self, other)
  263. name = f"{props[0].full_name} {op} {props[1].full_name}"
  264. if fn is None:
  265. name = format.wrap(name, "(")
  266. if fn is not None:
  267. name = f"{fn}({name})"
  268. return BaseVar(
  269. name=name,
  270. type_=type_,
  271. )
  272. def compare(self, op: str, other: Var) -> Var:
  273. """Compare two vars with inequalities.
  274. Args:
  275. op: The comparison operator.
  276. other: The other var to compare with.
  277. Returns:
  278. The comparison result.
  279. """
  280. return self.operation(op, other, bool)
  281. def __invert__(self) -> Var:
  282. """Invert a var.
  283. Returns:
  284. The inverted var.
  285. """
  286. return self.operation("!", type_=bool)
  287. def __neg__(self) -> Var:
  288. """Negate a var.
  289. Returns:
  290. The negated var.
  291. """
  292. return self.operation(fn="-")
  293. def __abs__(self) -> Var:
  294. """Get the absolute value of a var.
  295. Returns:
  296. A var with the absolute value.
  297. """
  298. return self.operation(fn="Math.abs")
  299. def length(self) -> Var:
  300. """Get the length of a list var.
  301. Returns:
  302. A var with the absolute value.
  303. Raises:
  304. TypeError: If the var is not a list.
  305. """
  306. if not types._issubclass(self.type_, List):
  307. raise TypeError(f"Cannot get length of non-list var {self}.")
  308. return BaseVar(
  309. name=f"{self.full_name}.length",
  310. type_=int,
  311. )
  312. def __eq__(self, other: Var) -> Var:
  313. """Perform an equality comparison.
  314. Args:
  315. other: The other var to compare with.
  316. Returns:
  317. A var representing the equality comparison.
  318. """
  319. return self.compare("===", other)
  320. def __ne__(self, other: Var) -> Var:
  321. """Perform an inequality comparison.
  322. Args:
  323. other: The other var to compare with.
  324. Returns:
  325. A var representing the inequality comparison.
  326. """
  327. return self.compare("!==", other)
  328. def __gt__(self, other: Var) -> Var:
  329. """Perform a greater than comparison.
  330. Args:
  331. other: The other var to compare with.
  332. Returns:
  333. A var representing the greater than comparison.
  334. """
  335. return self.compare(">", other)
  336. def __ge__(self, other: Var) -> Var:
  337. """Perform a greater than or equal to comparison.
  338. Args:
  339. other: The other var to compare with.
  340. Returns:
  341. A var representing the greater than or equal to comparison.
  342. """
  343. return self.compare(">=", other)
  344. def __lt__(self, other: Var) -> Var:
  345. """Perform a less than comparison.
  346. Args:
  347. other: The other var to compare with.
  348. Returns:
  349. A var representing the less than comparison.
  350. """
  351. return self.compare("<", other)
  352. def __le__(self, other: Var) -> Var:
  353. """Perform a less than or equal to comparison.
  354. Args:
  355. other: The other var to compare with.
  356. Returns:
  357. A var representing the less than or equal to comparison.
  358. """
  359. return self.compare("<=", other)
  360. def __add__(self, other: Var) -> Var:
  361. """Add two vars.
  362. Args:
  363. other: The other var to add.
  364. Returns:
  365. A var representing the sum.
  366. """
  367. return self.operation("+", other)
  368. def __radd__(self, other: Var) -> Var:
  369. """Add two vars.
  370. Args:
  371. other: The other var to add.
  372. Returns:
  373. A var representing the sum.
  374. """
  375. return self.operation("+", other, flip=True)
  376. def __sub__(self, other: Var) -> Var:
  377. """Subtract two vars.
  378. Args:
  379. other: The other var to subtract.
  380. Returns:
  381. A var representing the difference.
  382. """
  383. return self.operation("-", other)
  384. def __rsub__(self, other: Var) -> Var:
  385. """Subtract two vars.
  386. Args:
  387. other: The other var to subtract.
  388. Returns:
  389. A var representing the difference.
  390. """
  391. return self.operation("-", other, flip=True)
  392. def __mul__(self, other: Var) -> Var:
  393. """Multiply two vars.
  394. Args:
  395. other: The other var to multiply.
  396. Returns:
  397. A var representing the product.
  398. """
  399. return self.operation("*", other)
  400. def __rmul__(self, other: Var) -> Var:
  401. """Multiply two vars.
  402. Args:
  403. other: The other var to multiply.
  404. Returns:
  405. A var representing the product.
  406. """
  407. return self.operation("*", other, flip=True)
  408. def __pow__(self, other: Var) -> Var:
  409. """Raise a var to a power.
  410. Args:
  411. other: The power to raise to.
  412. Returns:
  413. A var representing the power.
  414. """
  415. return self.operation(",", other, fn="Math.pow")
  416. def __rpow__(self, other: Var) -> Var:
  417. """Raise a var to a power.
  418. Args:
  419. other: The power to raise to.
  420. Returns:
  421. A var representing the power.
  422. """
  423. return self.operation(",", other, flip=True, fn="Math.pow")
  424. def __truediv__(self, other: Var) -> Var:
  425. """Divide two vars.
  426. Args:
  427. other: The other var to divide.
  428. Returns:
  429. A var representing the quotient.
  430. """
  431. return self.operation("/", other)
  432. def __rtruediv__(self, other: Var) -> Var:
  433. """Divide two vars.
  434. Args:
  435. other: The other var to divide.
  436. Returns:
  437. A var representing the quotient.
  438. """
  439. return self.operation("/", other, flip=True)
  440. def __floordiv__(self, other: Var) -> Var:
  441. """Divide two vars.
  442. Args:
  443. other: The other var to divide.
  444. Returns:
  445. A var representing the quotient.
  446. """
  447. return self.operation("/", other, fn="Math.floor")
  448. def __mod__(self, other: Var) -> Var:
  449. """Get the remainder of two vars.
  450. Args:
  451. other: The other var to divide.
  452. Returns:
  453. A var representing the remainder.
  454. """
  455. return self.operation("%", other)
  456. def __rmod__(self, other: Var) -> Var:
  457. """Get the remainder of two vars.
  458. Args:
  459. other: The other var to divide.
  460. Returns:
  461. A var representing the remainder.
  462. """
  463. return self.operation("%", other, flip=True)
  464. def __and__(self, other: Var) -> Var:
  465. """Perform a logical and.
  466. Args:
  467. other: The other var to perform the logical and with.
  468. Returns:
  469. A var representing the logical and.
  470. """
  471. return self.operation("&&", other)
  472. def __rand__(self, other: Var) -> Var:
  473. """Perform a logical and.
  474. Args:
  475. other: The other var to perform the logical and with.
  476. Returns:
  477. A var representing the logical and.
  478. """
  479. return self.operation("&&", other, flip=True)
  480. def __or__(self, other: Var) -> Var:
  481. """Perform a logical or.
  482. Args:
  483. other: The other var to perform the logical or with.
  484. Returns:
  485. A var representing the logical or.
  486. """
  487. return self.operation("||", other)
  488. def __ror__(self, other: Var) -> Var:
  489. """Perform a logical or.
  490. Args:
  491. other: The other var to perform the logical or with.
  492. Returns:
  493. A var representing the logical or.
  494. """
  495. return self.operation("||", other, flip=True)
  496. def foreach(self, fn: Callable) -> Var:
  497. """Return a list of components. after doing a foreach on this var.
  498. Args:
  499. fn: The function to call on each component.
  500. Returns:
  501. A var representing foreach operation.
  502. """
  503. arg = BaseVar(
  504. name=get_unique_variable_name(),
  505. type_=self.type_,
  506. )
  507. return BaseVar(
  508. name=f"{self.full_name}.map(({arg.name}, i) => {fn(arg, key='i')})",
  509. type_=self.type_,
  510. )
  511. def to(self, type_: Type) -> Var:
  512. """Convert the type of the var.
  513. Args:
  514. type_: The type to convert to.
  515. Returns:
  516. The converted var.
  517. """
  518. return BaseVar(
  519. name=self.name,
  520. type_=type_,
  521. state=self.state,
  522. is_local=self.is_local,
  523. )
  524. @property
  525. def full_name(self) -> str:
  526. """Get the full name of the var.
  527. Returns:
  528. The full name of the var.
  529. """
  530. return self.name if self.state == "" else ".".join([self.state, self.name])
  531. def set_state(self, state: Type[State]) -> Any:
  532. """Set the state of the var.
  533. Args:
  534. state: The state to set.
  535. Returns:
  536. The var with the set state.
  537. """
  538. self.state = state.get_full_name()
  539. return self
  540. class BaseVar(Var, Base):
  541. """A base (non-computed) var of the app state."""
  542. # The name of the var.
  543. name: str
  544. # The type of the var.
  545. type_: Any
  546. # The name of the enclosing state.
  547. state: str = ""
  548. # Whether this is a local javascript variable.
  549. is_local: bool = False
  550. # Whether this var is a raw string.
  551. is_string: bool = False
  552. def __hash__(self) -> int:
  553. """Define a hash function for a var.
  554. Returns:
  555. The hash of the var.
  556. """
  557. return hash((self.name, str(self.type_)))
  558. def get_default_value(self) -> Any:
  559. """Get the default value of the var.
  560. Returns:
  561. The default value of the var.
  562. Raises:
  563. ImportError: If the var is a dataframe and pandas is not installed.
  564. """
  565. type_ = (
  566. self.type_.__origin__ if types.is_generic_alias(self.type_) else self.type_
  567. )
  568. if issubclass(type_, str):
  569. return ""
  570. if issubclass(type_, types.get_args(Union[int, float])):
  571. return 0
  572. if issubclass(type_, bool):
  573. return False
  574. if issubclass(type_, list):
  575. return []
  576. if issubclass(type_, dict):
  577. return {}
  578. if issubclass(type_, tuple):
  579. return ()
  580. if types.is_dataframe(type_):
  581. try:
  582. import pandas as pd
  583. return pd.DataFrame()
  584. except ImportError as e:
  585. raise ImportError(
  586. "Please install pandas to use dataframes in your app."
  587. ) from e
  588. return set() if issubclass(type_, set) else None
  589. def get_setter_name(self, include_state: bool = True) -> str:
  590. """Get the name of the var's generated setter function.
  591. Args:
  592. include_state: Whether to include the state name in the setter name.
  593. Returns:
  594. The name of the setter function.
  595. """
  596. setter = constants.SETTER_PREFIX + self.name
  597. if not include_state or self.state == "":
  598. return setter
  599. return ".".join((self.state, setter))
  600. def get_setter(self) -> Callable[[State, Any], None]:
  601. """Get the var's setter function.
  602. Returns:
  603. A function that that creates a setter for the var.
  604. """
  605. def setter(state: State, value: Any):
  606. """Get the setter for the var.
  607. Args:
  608. state: The state within which we add the setter function.
  609. value: The value to set.
  610. """
  611. setattr(state, self.name, value)
  612. setter.__qualname__ = self.get_setter_name()
  613. return setter
  614. class ComputedVar(property, Var):
  615. """A field with computed getters."""
  616. @property
  617. def name(self) -> str:
  618. """Get the name of the var.
  619. Returns:
  620. The name of the var.
  621. """
  622. assert self.fget is not None, "Var must have a getter."
  623. return self.fget.__name__
  624. @property
  625. def type_(self):
  626. """Get the type of the var.
  627. Returns:
  628. The type of the var.
  629. """
  630. if "return" in self.fget.__annotations__:
  631. return self.fget.__annotations__["return"]
  632. return Any
  633. class PCList(list):
  634. """A custom list that pynecone can detect its mutation."""
  635. def __init__(
  636. self,
  637. original_list: List,
  638. reassign_field: Callable = lambda _field_name: None,
  639. field_name: str = "",
  640. ):
  641. """Initialize PCList.
  642. Args:
  643. original_list (List): The original list
  644. reassign_field (Callable):
  645. The method in the parent state to reassign the field.
  646. Default to be a no-op function
  647. field_name (str): the name of field in the parent state
  648. """
  649. self._reassign_field = lambda: reassign_field(field_name)
  650. super().__init__(original_list)
  651. def append(self, *args, **kwargs):
  652. """Append.
  653. Args:
  654. args: The args passed.
  655. kwargs: The kwargs passed.
  656. """
  657. super().append(*args, **kwargs)
  658. self._reassign_field()
  659. def __setitem__(self, *args, **kwargs):
  660. """Set item.
  661. Args:
  662. args: The args passed.
  663. kwargs: The kwargs passed.
  664. """
  665. super().__setitem__(*args, **kwargs)
  666. self._reassign_field()
  667. def __delitem__(self, *args, **kwargs):
  668. """Delete item.
  669. Args:
  670. args: The args passed.
  671. kwargs: The kwargs passed.
  672. """
  673. super().__delitem__(*args, **kwargs)
  674. self._reassign_field()
  675. def clear(self, *args, **kwargs):
  676. """Remove all item from the list.
  677. Args:
  678. args: The args passed.
  679. kwargs: The kwargs passed.
  680. """
  681. super().clear(*args, **kwargs)
  682. self._reassign_field()
  683. def extend(self, *args, **kwargs):
  684. """Add all item of a list to the end of the list.
  685. Args:
  686. args: The args passed.
  687. kwargs: The kwargs passed.
  688. """
  689. super().extend(*args, **kwargs)
  690. self._reassign_field() if hasattr(self, "_reassign_field") else None
  691. def pop(self, *args, **kwargs):
  692. """Remove an element.
  693. Args:
  694. args: The args passed.
  695. kwargs: The kwargs passed.
  696. """
  697. super().pop(*args, **kwargs)
  698. self._reassign_field()
  699. def remove(self, *args, **kwargs):
  700. """Remove an element.
  701. Args:
  702. args: The args passed.
  703. kwargs: The kwargs passed.
  704. """
  705. super().remove(*args, **kwargs)
  706. self._reassign_field()
  707. class PCDict(dict):
  708. """A custom dict that pynecone can detect its mutation."""
  709. def __init__(
  710. self,
  711. original_dict: Dict,
  712. reassign_field: Callable = lambda _field_name: None,
  713. field_name: str = "",
  714. ):
  715. """Initialize PCDict.
  716. Args:
  717. original_dict: The original dict
  718. reassign_field:
  719. The method in the parent state to reassign the field.
  720. Default to be a no-op function
  721. field_name: the name of field in the parent state
  722. """
  723. super().__init__(original_dict)
  724. self._reassign_field = lambda: reassign_field(field_name)
  725. def clear(self):
  726. """Remove all item from the list."""
  727. super().clear()
  728. self._reassign_field()
  729. def setdefault(self, *args, **kwargs):
  730. """Return value of key if or set default.
  731. Args:
  732. args: The args passed.
  733. kwargs: The kwargs passed.
  734. """
  735. super().setdefault(*args, **kwargs)
  736. self._reassign_field()
  737. def popitem(self):
  738. """Pop last item."""
  739. super().popitem()
  740. self._reassign_field()
  741. def pop(self, k, d=None):
  742. """Remove an element.
  743. Args:
  744. k: The args passed.
  745. d: The kwargs passed.
  746. """
  747. super().pop(k, d)
  748. self._reassign_field()
  749. def update(self, *args, **kwargs):
  750. """Update the dict with another dict.
  751. Args:
  752. args: The args passed.
  753. kwargs: The kwargs passed.
  754. """
  755. super().update(*args, **kwargs)
  756. self._reassign_field()
  757. def __setitem__(self, *args, **kwargs):
  758. """Set an item in the dict.
  759. Args:
  760. args: The args passed.
  761. kwargs: The kwargs passed.
  762. """
  763. super().__setitem__(*args, **kwargs)
  764. self._reassign_field() if hasattr(self, "_reassign_field") else None
  765. def __delitem__(self, *args, **kwargs):
  766. """Delete an item in the dict.
  767. Args:
  768. args: The args passed.
  769. kwargs: The kwargs passed.
  770. """
  771. super().__delitem__(*args, **kwargs)
  772. self._reassign_field()