object.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627
  1. """Classes for immutable object vars."""
  2. from __future__ import annotations
  3. import dataclasses
  4. import sys
  5. import typing
  6. from functools import cached_property
  7. from typing import Any, Dict, List, Tuple, Type, Union
  8. from reflex.experimental.vars.base import ImmutableVar, LiteralVar
  9. from reflex.experimental.vars.sequence import ArrayVar, unionize
  10. from reflex.vars import ImmutableVarData, Var, VarData
  11. class ObjectVar(ImmutableVar):
  12. """Base class for immutable object vars."""
  13. def _key_type(self) -> Type:
  14. """Get the type of the keys of the object.
  15. Returns:
  16. The type of the keys of the object.
  17. """
  18. return ImmutableVar
  19. def _value_type(self) -> Type:
  20. """Get the type of the values of the object.
  21. Returns:
  22. The type of the values of the object.
  23. """
  24. return ImmutableVar
  25. def keys(self) -> ObjectKeysOperation:
  26. """Get the keys of the object.
  27. Returns:
  28. The keys of the object.
  29. """
  30. return ObjectKeysOperation(self)
  31. def values(self) -> ObjectValuesOperation:
  32. """Get the values of the object.
  33. Returns:
  34. The values of the object.
  35. """
  36. return ObjectValuesOperation(self)
  37. def entries(self) -> ObjectEntriesOperation:
  38. """Get the entries of the object.
  39. Returns:
  40. The entries of the object.
  41. """
  42. return ObjectEntriesOperation(self)
  43. def merge(self, other: ObjectVar) -> ObjectMergeOperation:
  44. """Merge two objects.
  45. Args:
  46. other: The other object to merge.
  47. Returns:
  48. The merged object.
  49. """
  50. return ObjectMergeOperation(self, other)
  51. def __getitem__(self, key: Var | Any) -> ImmutableVar:
  52. """Get an item from the object.
  53. Args:
  54. key: The key to get from the object.
  55. Returns:
  56. The item from the object.
  57. """
  58. return ObjectItemOperation(self, key).guess_type()
  59. def __getattr__(self, name) -> ObjectItemOperation:
  60. """Get an attribute of the var.
  61. Args:
  62. name: The name of the attribute.
  63. Returns:
  64. The attribute of the var.
  65. """
  66. return ObjectItemOperation(self, name)
  67. @dataclasses.dataclass(
  68. eq=False,
  69. frozen=True,
  70. **{"slots": True} if sys.version_info >= (3, 10) else {},
  71. )
  72. class LiteralObjectVar(LiteralVar, ObjectVar):
  73. """Base class for immutable literal object vars."""
  74. _var_value: Dict[Union[Var, Any], Union[Var, Any]] = dataclasses.field(
  75. default_factory=dict
  76. )
  77. def __init__(
  78. self,
  79. _var_value: dict[Var | Any, Var | Any],
  80. _var_type: Type | None = None,
  81. _var_data: VarData | None = None,
  82. ):
  83. """Initialize the object var.
  84. Args:
  85. _var_value: The value of the var.
  86. _var_type: The type of the var.
  87. _var_data: Additional hooks and imports associated with the Var.
  88. """
  89. super(LiteralObjectVar, self).__init__(
  90. _var_name="",
  91. _var_type=(
  92. Dict[
  93. unionize(*map(type, _var_value.keys())),
  94. unionize(*map(type, _var_value.values())),
  95. ]
  96. if _var_type is None
  97. else _var_type
  98. ),
  99. _var_data=ImmutableVarData.merge(_var_data),
  100. )
  101. object.__setattr__(
  102. self,
  103. "_var_value",
  104. _var_value,
  105. )
  106. object.__delattr__(self, "_var_name")
  107. def _key_type(self) -> Type:
  108. """Get the type of the keys of the object.
  109. Returns:
  110. The type of the keys of the object.
  111. """
  112. args_list = typing.get_args(self._var_type)
  113. return args_list[0] if args_list else Any
  114. def _value_type(self) -> Type:
  115. """Get the type of the values of the object.
  116. Returns:
  117. The type of the values of the object.
  118. """
  119. args_list = typing.get_args(self._var_type)
  120. return args_list[1] if args_list else Any
  121. def __getattr__(self, name):
  122. """Get an attribute of the var.
  123. Args:
  124. name: The name of the attribute.
  125. Returns:
  126. The attribute of the var.
  127. """
  128. if name == "_var_name":
  129. return self._cached_var_name
  130. return super(type(self), self).__getattr__(name)
  131. @cached_property
  132. def _cached_var_name(self) -> str:
  133. """The name of the var.
  134. Returns:
  135. The name of the var.
  136. """
  137. return (
  138. "({ "
  139. + ", ".join(
  140. [
  141. f"[{str(LiteralVar.create(key))}] : {str(LiteralVar.create(value))}"
  142. for key, value in self._var_value.items()
  143. ]
  144. )
  145. + " })"
  146. )
  147. @cached_property
  148. def _cached_get_all_var_data(self) -> ImmutableVarData | None:
  149. """Get all VarData associated with the Var.
  150. Returns:
  151. The VarData of the components and all of its children.
  152. """
  153. return ImmutableVarData.merge(
  154. *[
  155. value._get_all_var_data()
  156. for key, value in self._var_value
  157. if isinstance(value, Var)
  158. ],
  159. *[
  160. key._get_all_var_data()
  161. for key, value in self._var_value
  162. if isinstance(key, Var)
  163. ],
  164. self._var_data,
  165. )
  166. def _get_all_var_data(self) -> ImmutableVarData | None:
  167. """Wrapper method for cached property.
  168. Returns:
  169. The VarData of the components and all of its children.
  170. """
  171. return self._cached_get_all_var_data
  172. def json(self) -> str:
  173. """Get the JSON representation of the object.
  174. Returns:
  175. The JSON representation of the object.
  176. """
  177. return (
  178. "{"
  179. + ", ".join(
  180. [
  181. f"{LiteralVar.create(key).json()}:{LiteralVar.create(value).json()}"
  182. for key, value in self._var_value.items()
  183. ]
  184. )
  185. + "}"
  186. )
  187. def __hash__(self) -> int:
  188. """Get the hash of the var.
  189. Returns:
  190. The hash of the var.
  191. """
  192. return hash((self.__class__.__name__, self._var_name))
  193. @dataclasses.dataclass(
  194. eq=False,
  195. frozen=True,
  196. **{"slots": True} if sys.version_info >= (3, 10) else {},
  197. )
  198. class ObjectToArrayOperation(ArrayVar):
  199. """Base class for object to array operations."""
  200. value: ObjectVar = dataclasses.field(default_factory=lambda: LiteralObjectVar({}))
  201. def __init__(
  202. self,
  203. _var_value: ObjectVar,
  204. _var_type: Type = list,
  205. _var_data: VarData | None = None,
  206. ):
  207. """Initialize the object to array operation.
  208. Args:
  209. _var_value: The value of the operation.
  210. _var_data: Additional hooks and imports associated with the operation.
  211. """
  212. super(ObjectToArrayOperation, self).__init__(
  213. _var_name="",
  214. _var_type=_var_type,
  215. _var_data=ImmutableVarData.merge(_var_data),
  216. )
  217. object.__setattr__(self, "value", _var_value)
  218. object.__delattr__(self, "_var_name")
  219. @cached_property
  220. def _cached_var_name(self) -> str:
  221. """The name of the operation.
  222. Raises:
  223. NotImplementedError: Must implement _cached_var_name.
  224. """
  225. raise NotImplementedError(
  226. "ObjectToArrayOperation must implement _cached_var_name"
  227. )
  228. def __getattr__(self, name):
  229. """Get an attribute of the operation.
  230. Args:
  231. name: The name of the attribute.
  232. Returns:
  233. The attribute of the operation.
  234. """
  235. if name == "_var_name":
  236. return self._cached_var_name
  237. return super(type(self), self).__getattr__(name)
  238. @cached_property
  239. def _cached_get_all_var_data(self) -> ImmutableVarData | None:
  240. """Get all VarData associated with the operation.
  241. Returns:
  242. The VarData of the components and all of its children.
  243. """
  244. return ImmutableVarData.merge(
  245. self.value._get_all_var_data(),
  246. self._var_data,
  247. )
  248. def _get_all_var_data(self) -> ImmutableVarData | None:
  249. """Wrapper method for cached property.
  250. Returns:
  251. The VarData of the components and all of its children.
  252. """
  253. return self._cached_get_all_var_data
  254. class ObjectKeysOperation(ObjectToArrayOperation):
  255. """Operation to get the keys of an object."""
  256. def __init__(
  257. self,
  258. value: ObjectVar,
  259. _var_data: VarData | None = None,
  260. ):
  261. """Initialize the object keys operation.
  262. Args:
  263. value: The value of the operation.
  264. _var_data: Additional hooks and imports associated with the operation.
  265. """
  266. super(ObjectKeysOperation, self).__init__(
  267. value, List[value._key_type()], _var_data
  268. )
  269. @cached_property
  270. def _cached_var_name(self) -> str:
  271. """The name of the operation.
  272. Returns:
  273. The name of the operation.
  274. """
  275. return f"Object.keys({self.value._var_name})"
  276. class ObjectValuesOperation(ObjectToArrayOperation):
  277. """Operation to get the values of an object."""
  278. def __init__(
  279. self,
  280. value: ObjectVar,
  281. _var_data: VarData | None = None,
  282. ):
  283. """Initialize the object values operation.
  284. Args:
  285. value: The value of the operation.
  286. _var_data: Additional hooks and imports associated with the operation.
  287. """
  288. super(ObjectValuesOperation, self).__init__(
  289. value, List[value._value_type()], _var_data
  290. )
  291. @cached_property
  292. def _cached_var_name(self) -> str:
  293. """The name of the operation.
  294. Returns:
  295. The name of the operation.
  296. """
  297. return f"Object.values({self.value._var_name})"
  298. class ObjectEntriesOperation(ObjectToArrayOperation):
  299. """Operation to get the entries of an object."""
  300. def __init__(
  301. self,
  302. value: ObjectVar,
  303. _var_data: VarData | None = None,
  304. ):
  305. """Initialize the object entries operation.
  306. Args:
  307. value: The value of the operation.
  308. _var_data: Additional hooks and imports associated with the operation.
  309. """
  310. super(ObjectEntriesOperation, self).__init__(
  311. value, List[Tuple[value._key_type(), value._value_type()]], _var_data
  312. )
  313. @cached_property
  314. def _cached_var_name(self) -> str:
  315. """The name of the operation.
  316. Returns:
  317. The name of the operation.
  318. """
  319. return f"Object.entries({self.value._var_name})"
  320. @dataclasses.dataclass(
  321. eq=False,
  322. frozen=True,
  323. **{"slots": True} if sys.version_info >= (3, 10) else {},
  324. )
  325. class ObjectMergeOperation(ObjectVar):
  326. """Operation to merge two objects."""
  327. left: ObjectVar = dataclasses.field(default_factory=lambda: LiteralObjectVar({}))
  328. right: ObjectVar = dataclasses.field(default_factory=lambda: LiteralObjectVar({}))
  329. def __init__(
  330. self,
  331. left: ObjectVar,
  332. right: ObjectVar,
  333. _var_data: VarData | None = None,
  334. ):
  335. """Initialize the object merge operation.
  336. Args:
  337. left: The left object to merge.
  338. right: The right object to merge.
  339. _var_data: Additional hooks and imports associated with the operation.
  340. """
  341. super(ObjectMergeOperation, self).__init__(
  342. _var_name="",
  343. _var_type=left._var_type,
  344. _var_data=ImmutableVarData.merge(_var_data),
  345. )
  346. object.__setattr__(self, "left", left)
  347. object.__setattr__(self, "right", right)
  348. object.__delattr__(self, "_var_name")
  349. @cached_property
  350. def _cached_var_name(self) -> str:
  351. """The name of the operation.
  352. Returns:
  353. The name of the operation.
  354. """
  355. return f"Object.assign({self.left._var_name}, {self.right._var_name})"
  356. def __getattr__(self, name):
  357. """Get an attribute of the operation.
  358. Args:
  359. name: The name of the attribute.
  360. Returns:
  361. The attribute of the operation.
  362. """
  363. if name == "_var_name":
  364. return self._cached_var_name
  365. return super(type(self), self).__getattr__(name)
  366. @cached_property
  367. def _cached_get_all_var_data(self) -> ImmutableVarData | None:
  368. """Get all VarData associated with the operation.
  369. Returns:
  370. The VarData of the components and all of its children.
  371. """
  372. return ImmutableVarData.merge(
  373. self.left._get_all_var_data(),
  374. self.right._get_all_var_data(),
  375. self._var_data,
  376. )
  377. def _get_all_var_data(self) -> ImmutableVarData | None:
  378. """Wrapper method for cached property.
  379. Returns:
  380. The VarData of the components and all of its children.
  381. """
  382. return self._cached_get_all_var_data
  383. @dataclasses.dataclass(
  384. eq=False,
  385. frozen=True,
  386. **{"slots": True} if sys.version_info >= (3, 10) else {},
  387. )
  388. class ObjectItemOperation(ImmutableVar):
  389. """Operation to get an item from an object."""
  390. value: ObjectVar = dataclasses.field(default_factory=lambda: LiteralObjectVar({}))
  391. key: Var | Any = dataclasses.field(default_factory=lambda: LiteralVar.create(None))
  392. def __init__(
  393. self,
  394. value: ObjectVar,
  395. key: Var | Any,
  396. _var_data: VarData | None = None,
  397. ):
  398. """Initialize the object item operation.
  399. Args:
  400. value: The value of the operation.
  401. key: The key to get from the object.
  402. _var_data: Additional hooks and imports associated with the operation.
  403. """
  404. super(ObjectItemOperation, self).__init__(
  405. _var_name="",
  406. _var_type=value._value_type(),
  407. _var_data=ImmutableVarData.merge(_var_data),
  408. )
  409. object.__setattr__(self, "value", value)
  410. object.__setattr__(
  411. self, "key", key if isinstance(key, Var) else LiteralVar.create(key)
  412. )
  413. object.__delattr__(self, "_var_name")
  414. @cached_property
  415. def _cached_var_name(self) -> str:
  416. """The name of the operation.
  417. Returns:
  418. The name of the operation.
  419. """
  420. return f"{str(self.value)}[{str(self.key)}]"
  421. def __getattr__(self, name):
  422. """Get an attribute of the operation.
  423. Args:
  424. name: The name of the attribute.
  425. Returns:
  426. The attribute of the operation.
  427. """
  428. if name == "_var_name":
  429. return self._cached_var_name
  430. return super(type(self), self).__getattr__(name)
  431. @cached_property
  432. def _cached_get_all_var_data(self) -> ImmutableVarData | None:
  433. """Get all VarData associated with the operation.
  434. Returns:
  435. The VarData of the components and all of its children.
  436. """
  437. return ImmutableVarData.merge(
  438. self.value._get_all_var_data(),
  439. self.key._get_all_var_data(),
  440. self._var_data,
  441. )
  442. def _get_all_var_data(self) -> ImmutableVarData | None:
  443. """Wrapper method for cached property.
  444. Returns:
  445. The VarData of the components and all of its children.
  446. """
  447. return self._cached_get_all_var_data
  448. @dataclasses.dataclass(
  449. eq=False,
  450. frozen=True,
  451. **{"slots": True} if sys.version_info >= (3, 10) else {},
  452. )
  453. class ToObjectOperation(ObjectVar):
  454. """Operation to convert a var to an object."""
  455. _original_var: Var = dataclasses.field(default_factory=lambda: LiteralObjectVar({}))
  456. def __init__(
  457. self,
  458. _original_var: Var,
  459. _var_type: Type = dict,
  460. _var_data: VarData | None = None,
  461. ):
  462. """Initialize the to object operation.
  463. Args:
  464. _original_var: The original var to convert.
  465. _var_type: The type of the var.
  466. _var_data: Additional hooks and imports associated with the operation.
  467. """
  468. super(ToObjectOperation, self).__init__(
  469. _var_name="",
  470. _var_type=_var_type,
  471. _var_data=ImmutableVarData.merge(_var_data),
  472. )
  473. object.__setattr__(self, "_original_var", _original_var)
  474. object.__delattr__(self, "_var_name")
  475. @cached_property
  476. def _cached_var_name(self) -> str:
  477. """The name of the operation.
  478. Returns:
  479. The name of the operation.
  480. """
  481. return str(self._original_var)
  482. def __getattr__(self, name):
  483. """Get an attribute of the operation.
  484. Args:
  485. name: The name of the attribute.
  486. Returns:
  487. The attribute of the operation.
  488. """
  489. if name == "_var_name":
  490. return self._cached_var_name
  491. return super(type(self), self).__getattr__(name)
  492. @cached_property
  493. def _cached_get_all_var_data(self) -> ImmutableVarData | None:
  494. """Get all VarData associated with the operation.
  495. Returns:
  496. The VarData of the components and all of its children.
  497. """
  498. return ImmutableVarData.merge(
  499. self._original_var._get_all_var_data(),
  500. self._var_data,
  501. )
  502. def _get_all_var_data(self) -> ImmutableVarData | None:
  503. """Wrapper method for cached property.
  504. Returns:
  505. The VarData of the components and all of its children.
  506. """
  507. return self._cached_get_all_var_data