var.py 25 KB

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