object.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620
  1. """Classes for immutable object vars."""
  2. from __future__ import annotations
  3. import collections.abc
  4. import dataclasses
  5. import typing
  6. from collections.abc import Mapping
  7. from inspect import isclass
  8. from typing import (
  9. Any,
  10. NoReturn,
  11. TypeVar,
  12. get_args,
  13. get_type_hints,
  14. is_typeddict,
  15. overload,
  16. )
  17. from rich.markup import escape
  18. from reflex.utils import types
  19. from reflex.utils.exceptions import VarAttributeError
  20. from reflex.utils.types import (
  21. GenericType,
  22. get_attribute_access_type,
  23. get_origin,
  24. safe_issubclass,
  25. unionize,
  26. )
  27. from .base import (
  28. CachedVarOperation,
  29. LiteralVar,
  30. Var,
  31. VarData,
  32. cached_property_no_lock,
  33. figure_out_type,
  34. var_operation,
  35. var_operation_return,
  36. )
  37. from .number import BooleanVar, NumberVar, raise_unsupported_operand_types
  38. from .sequence import ArrayVar, StringVar
  39. OBJECT_TYPE = TypeVar("OBJECT_TYPE", covariant=True)
  40. KEY_TYPE = TypeVar("KEY_TYPE")
  41. VALUE_TYPE = TypeVar("VALUE_TYPE")
  42. ARRAY_INNER_TYPE = TypeVar("ARRAY_INNER_TYPE")
  43. OTHER_KEY_TYPE = TypeVar("OTHER_KEY_TYPE")
  44. def _determine_value_type(var_type: GenericType):
  45. origin_var_type = get_origin(var_type) or var_type
  46. if origin_var_type in types.UnionTypes:
  47. return unionize(
  48. *[
  49. _determine_value_type(arg)
  50. for arg in get_args(var_type)
  51. if arg is not type(None)
  52. ]
  53. )
  54. if is_typeddict(origin_var_type) or dataclasses.is_dataclass(origin_var_type):
  55. annotations = get_type_hints(origin_var_type)
  56. return unionize(*annotations.values())
  57. if origin_var_type in [dict, Mapping, collections.abc.Mapping]:
  58. args = get_args(var_type)
  59. return args[1] if args else Any
  60. return Any
  61. class ObjectVar(Var[OBJECT_TYPE], python_types=Mapping):
  62. """Base class for immutable object vars."""
  63. def _key_type(self) -> type:
  64. """Get the type of the keys of the object.
  65. Returns:
  66. The type of the keys of the object.
  67. """
  68. return str
  69. @overload
  70. def _value_type(
  71. self: ObjectVar[Mapping[Any, VALUE_TYPE]],
  72. ) -> type[VALUE_TYPE]: ...
  73. @overload
  74. def _value_type(self) -> GenericType: ...
  75. def _value_type(self) -> GenericType:
  76. """Get the type of the values of the object.
  77. Returns:
  78. The type of the values of the object.
  79. """
  80. return _determine_value_type(self._var_type)
  81. def keys(self) -> ArrayVar[list[str]]:
  82. """Get the keys of the object.
  83. Returns:
  84. The keys of the object.
  85. """
  86. return object_keys_operation(self)
  87. @overload
  88. def values(
  89. self: ObjectVar[Mapping[Any, VALUE_TYPE]],
  90. ) -> ArrayVar[list[VALUE_TYPE]]: ...
  91. @overload
  92. def values(self) -> ArrayVar: ...
  93. def values(self) -> ArrayVar:
  94. """Get the values of the object.
  95. Returns:
  96. The values of the object.
  97. """
  98. return object_values_operation(self)
  99. @overload
  100. def entries(
  101. self: ObjectVar[Mapping[Any, VALUE_TYPE]],
  102. ) -> ArrayVar[list[tuple[str, VALUE_TYPE]]]: ...
  103. @overload
  104. def entries(self) -> ArrayVar: ...
  105. def entries(self) -> ArrayVar:
  106. """Get the entries of the object.
  107. Returns:
  108. The entries of the object.
  109. """
  110. return object_entries_operation(self)
  111. items = entries
  112. def length(self) -> NumberVar[int]:
  113. """Get the length of the object.
  114. Returns:
  115. The length of the object.
  116. """
  117. return self.keys().length()
  118. def merge(self, other: ObjectVar):
  119. """Merge two objects.
  120. Args:
  121. other: The other object to merge.
  122. Returns:
  123. The merged object.
  124. """
  125. return object_merge_operation(self, other)
  126. # NoReturn is used here to catch when key value is Any
  127. @overload
  128. def __getitem__( # pyright: ignore [reportOverlappingOverload]
  129. self: ObjectVar[Mapping[Any, NoReturn]],
  130. key: Var | Any,
  131. ) -> Var: ...
  132. @overload
  133. def __getitem__(
  134. self: (ObjectVar[Mapping[Any, bool]]),
  135. key: Var | Any,
  136. ) -> BooleanVar: ...
  137. @overload
  138. def __getitem__(
  139. self: (
  140. ObjectVar[Mapping[Any, int]]
  141. | ObjectVar[Mapping[Any, float]]
  142. | ObjectVar[Mapping[Any, int | float]]
  143. ),
  144. key: Var | Any,
  145. ) -> NumberVar: ...
  146. @overload
  147. def __getitem__(
  148. self: ObjectVar[Mapping[Any, str]],
  149. key: Var | Any,
  150. ) -> StringVar: ...
  151. @overload
  152. def __getitem__(
  153. self: ObjectVar[Mapping[Any, list[ARRAY_INNER_TYPE]]],
  154. key: Var | Any,
  155. ) -> ArrayVar[list[ARRAY_INNER_TYPE]]: ...
  156. @overload
  157. def __getitem__(
  158. self: ObjectVar[Mapping[Any, tuple[ARRAY_INNER_TYPE, ...]]],
  159. key: Var | Any,
  160. ) -> ArrayVar[tuple[ARRAY_INNER_TYPE, ...]]: ...
  161. @overload
  162. def __getitem__(
  163. self: ObjectVar[Mapping[Any, Mapping[OTHER_KEY_TYPE, VALUE_TYPE]]],
  164. key: Var | Any,
  165. ) -> ObjectVar[Mapping[OTHER_KEY_TYPE, VALUE_TYPE]]: ...
  166. @overload
  167. def __getitem__(
  168. self: ObjectVar[Mapping[Any, VALUE_TYPE]],
  169. key: Var | Any,
  170. ) -> Var[VALUE_TYPE]: ...
  171. def __getitem__(self, key: Var | Any) -> Var:
  172. """Get an item from the object.
  173. Args:
  174. key: The key to get from the object.
  175. Returns:
  176. The item from the object.
  177. """
  178. from .sequence import LiteralStringVar
  179. if not isinstance(key, (StringVar, str, int, NumberVar)) or (
  180. isinstance(key, NumberVar) and key._is_strict_float()
  181. ):
  182. raise_unsupported_operand_types("[]", (type(self), type(key)))
  183. if isinstance(key, str) and isinstance(Var.create(key), LiteralStringVar):
  184. return self.__getattr__(key)
  185. return ObjectItemOperation.create(self, key).guess_type()
  186. def get(self, key: Var | Any, default: Var | Any | None = None) -> Var:
  187. """Get an item from the object.
  188. Args:
  189. key: The key to get from the object.
  190. default: The default value if the key is not found.
  191. Returns:
  192. The item from the object.
  193. """
  194. from reflex.components.core.cond import cond
  195. if default is None:
  196. default = Var.create(None)
  197. value = self.__getitem__(key) # pyright: ignore[reportUnknownVariableType,reportAttributeAccessIssue,reportUnknownMemberType]
  198. return cond( # pyright: ignore[reportUnknownVariableType]
  199. value,
  200. value,
  201. default,
  202. )
  203. # NoReturn is used here to catch when key value is Any
  204. @overload
  205. def __getattr__( # pyright: ignore [reportOverlappingOverload]
  206. self: ObjectVar[Mapping[Any, NoReturn]],
  207. name: str,
  208. ) -> Var: ...
  209. @overload
  210. def __getattr__(
  211. self: (
  212. ObjectVar[Mapping[Any, int]]
  213. | ObjectVar[Mapping[Any, float]]
  214. | ObjectVar[Mapping[Any, int | float]]
  215. ),
  216. name: str,
  217. ) -> NumberVar: ...
  218. @overload
  219. def __getattr__(
  220. self: ObjectVar[Mapping[Any, str]],
  221. name: str,
  222. ) -> StringVar: ...
  223. @overload
  224. def __getattr__(
  225. self: ObjectVar[Mapping[Any, list[ARRAY_INNER_TYPE]]],
  226. name: str,
  227. ) -> ArrayVar[list[ARRAY_INNER_TYPE]]: ...
  228. @overload
  229. def __getattr__(
  230. self: ObjectVar[Mapping[Any, tuple[ARRAY_INNER_TYPE, ...]]],
  231. name: str,
  232. ) -> ArrayVar[tuple[ARRAY_INNER_TYPE, ...]]: ...
  233. @overload
  234. def __getattr__(
  235. self: ObjectVar[Mapping[Any, Mapping[OTHER_KEY_TYPE, VALUE_TYPE]]],
  236. name: str,
  237. ) -> ObjectVar[Mapping[OTHER_KEY_TYPE, VALUE_TYPE]]: ...
  238. @overload
  239. def __getattr__(
  240. self: ObjectVar,
  241. name: str,
  242. ) -> ObjectItemOperation: ...
  243. def __getattr__(self, name: str) -> Var:
  244. """Get an attribute of the var.
  245. Args:
  246. name: The name of the attribute.
  247. Raises:
  248. VarAttributeError: The State var has no such attribute or may have been annotated wrongly.
  249. Returns:
  250. The attribute of the var.
  251. """
  252. if name.startswith("__") and name.endswith("__"):
  253. return getattr(super(type(self), self), name)
  254. var_type = self._var_type
  255. var_type = types.value_inside_optional(var_type)
  256. fixed_type = get_origin(var_type) or var_type
  257. if (
  258. is_typeddict(fixed_type)
  259. or (isclass(fixed_type) and not safe_issubclass(fixed_type, Mapping))
  260. or (fixed_type in types.UnionTypes)
  261. ):
  262. attribute_type = get_attribute_access_type(var_type, name)
  263. if attribute_type is None:
  264. raise VarAttributeError(
  265. f"The State var `{self!s}` of type {escape(str(self._var_type))} has no attribute '{name}' or may have been annotated "
  266. f"wrongly."
  267. )
  268. return ObjectItemOperation.create(self, name, attribute_type).guess_type()
  269. else:
  270. return ObjectItemOperation.create(self, name).guess_type()
  271. def contains(self, key: Var | Any) -> BooleanVar:
  272. """Check if the object contains a key.
  273. Args:
  274. key: The key to check.
  275. Returns:
  276. The result of the check.
  277. """
  278. return object_has_own_property_operation(self, key)
  279. @dataclasses.dataclass(
  280. eq=False,
  281. frozen=True,
  282. slots=True,
  283. )
  284. class LiteralObjectVar(CachedVarOperation, ObjectVar[OBJECT_TYPE], LiteralVar):
  285. """Base class for immutable literal object vars."""
  286. _var_value: Mapping[Var | Any, Var | Any] = dataclasses.field(default_factory=dict)
  287. def _key_type(self) -> type:
  288. """Get the type of the keys of the object.
  289. Returns:
  290. The type of the keys of the object.
  291. """
  292. args_list = typing.get_args(self._var_type)
  293. return args_list[0] if args_list else Any # pyright: ignore [reportReturnType]
  294. def _value_type(self) -> type:
  295. """Get the type of the values of the object.
  296. Returns:
  297. The type of the values of the object.
  298. """
  299. args_list = typing.get_args(self._var_type)
  300. return args_list[1] if args_list else Any # pyright: ignore [reportReturnType]
  301. @cached_property_no_lock
  302. def _cached_var_name(self) -> str:
  303. """The name of the var.
  304. Returns:
  305. The name of the var.
  306. """
  307. return (
  308. "({ "
  309. + ", ".join(
  310. [
  311. f"[{LiteralVar.create(key)!s}] : {LiteralVar.create(value)!s}"
  312. for key, value in self._var_value.items()
  313. ]
  314. )
  315. + " })"
  316. )
  317. def json(self) -> str:
  318. """Get the JSON representation of the object.
  319. Returns:
  320. The JSON representation of the object.
  321. Raises:
  322. TypeError: The keys and values of the object must be literal vars to get the JSON representation
  323. """
  324. keys_and_values = []
  325. for key, value in self._var_value.items():
  326. key = LiteralVar.create(key)
  327. value = LiteralVar.create(value)
  328. if not isinstance(key, LiteralVar) or not isinstance(value, LiteralVar):
  329. raise TypeError(
  330. "The keys and values of the object must be literal vars to get the JSON representation."
  331. )
  332. keys_and_values.append(f"{key.json()}:{value.json()}")
  333. return "{" + ", ".join(keys_and_values) + "}"
  334. def __hash__(self) -> int:
  335. """Get the hash of the var.
  336. Returns:
  337. The hash of the var.
  338. """
  339. return hash((type(self).__name__, self._js_expr))
  340. @cached_property_no_lock
  341. def _cached_get_all_var_data(self) -> VarData | None:
  342. """Get all the var data.
  343. Returns:
  344. The var data.
  345. """
  346. return VarData.merge(
  347. *[LiteralVar.create(var)._get_all_var_data() for var in self._var_value],
  348. *[
  349. LiteralVar.create(var)._get_all_var_data()
  350. for var in self._var_value.values()
  351. ],
  352. self._var_data,
  353. )
  354. @classmethod
  355. def create(
  356. cls,
  357. _var_value: Mapping,
  358. _var_type: type[OBJECT_TYPE] | None = None,
  359. _var_data: VarData | None = None,
  360. ) -> LiteralObjectVar[OBJECT_TYPE]:
  361. """Create the literal object var.
  362. Args:
  363. _var_value: The value of the var.
  364. _var_type: The type of the var.
  365. _var_data: Additional hooks and imports associated with the Var.
  366. Returns:
  367. The literal object var.
  368. """
  369. return LiteralObjectVar(
  370. _js_expr="",
  371. _var_type=(figure_out_type(_var_value) if _var_type is None else _var_type),
  372. _var_data=_var_data,
  373. _var_value=_var_value,
  374. )
  375. @var_operation
  376. def object_keys_operation(value: ObjectVar):
  377. """Get the keys of an object.
  378. Args:
  379. value: The object to get the keys from.
  380. Returns:
  381. The keys of the object.
  382. """
  383. if not types.is_optional(value._var_type):
  384. return var_operation_return(
  385. js_expression=f"Object.keys({value})",
  386. var_type=list[str],
  387. )
  388. return var_operation_return(
  389. js_expression=f"((value) => value ?? undefined === undefined ? undefined : Object.keys(value))({value})",
  390. var_type=(list[str] | None),
  391. )
  392. @var_operation
  393. def object_values_operation(value: ObjectVar):
  394. """Get the values of an object.
  395. Args:
  396. value: The object to get the values from.
  397. Returns:
  398. The values of the object.
  399. """
  400. if not types.is_optional(value._var_type):
  401. return var_operation_return(
  402. js_expression=f"Object.values({value})",
  403. var_type=list[value._value_type()],
  404. )
  405. return var_operation_return(
  406. js_expression=f"((value) => value ?? undefined === undefined ? undefined : Object.values(value))({value})",
  407. var_type=(list[value._value_type()] | None),
  408. )
  409. @var_operation
  410. def object_entries_operation(value: ObjectVar):
  411. """Get the entries of an object.
  412. Args:
  413. value: The object to get the entries from.
  414. Returns:
  415. The entries of the object.
  416. """
  417. if not types.is_optional(value._var_type):
  418. return var_operation_return(
  419. js_expression=f"Object.entries({value})",
  420. var_type=list[tuple[str, value._value_type()]],
  421. )
  422. return var_operation_return(
  423. js_expression=f"((value) => value ?? undefined === undefined ? undefined : Object.entries(value))({value})",
  424. var_type=(list[tuple[str, value._value_type()]] | None),
  425. )
  426. @var_operation
  427. def object_merge_operation(lhs: ObjectVar, rhs: ObjectVar):
  428. """Merge two objects.
  429. Args:
  430. lhs: The first object to merge.
  431. rhs: The second object to merge.
  432. Returns:
  433. The merged object.
  434. """
  435. return var_operation_return(
  436. js_expression=f"({{...{lhs}, ...{rhs}}})",
  437. var_type=Mapping[
  438. lhs._key_type() | rhs._key_type(),
  439. lhs._value_type() | rhs._value_type(),
  440. ],
  441. )
  442. @dataclasses.dataclass(
  443. eq=False,
  444. frozen=True,
  445. slots=True,
  446. )
  447. class ObjectItemOperation(CachedVarOperation, Var):
  448. """Operation to get an item from an object."""
  449. _object: ObjectVar = dataclasses.field(
  450. default_factory=lambda: LiteralObjectVar.create({})
  451. )
  452. _key: Var | Any = dataclasses.field(default_factory=lambda: LiteralVar.create(None))
  453. @cached_property_no_lock
  454. def _cached_var_name(self) -> str:
  455. """The name of the operation.
  456. Returns:
  457. The name of the operation.
  458. """
  459. if types.is_optional(self._object._var_type):
  460. return f"{self._object!s}?.[{self._key!s}]"
  461. return f"{self._object!s}[{self._key!s}]"
  462. @classmethod
  463. def create(
  464. cls,
  465. object: ObjectVar,
  466. key: Var | Any,
  467. _var_type: GenericType | None = None,
  468. _var_data: VarData | None = None,
  469. ) -> ObjectItemOperation:
  470. """Create the object item operation.
  471. Args:
  472. object: The object to get the item from.
  473. key: The key to get from the object.
  474. _var_type: The type of the item.
  475. _var_data: Additional hooks and imports associated with the operation.
  476. Returns:
  477. The object item operation.
  478. """
  479. return cls(
  480. _js_expr="",
  481. _var_type=object._value_type() if _var_type is None else _var_type,
  482. _var_data=_var_data,
  483. _object=object,
  484. _key=key if isinstance(key, Var) else LiteralVar.create(key),
  485. )
  486. @var_operation
  487. def object_has_own_property_operation(object: ObjectVar, key: Var):
  488. """Check if an object has a key.
  489. Args:
  490. object: The object to check.
  491. key: The key to check.
  492. Returns:
  493. The result of the check.
  494. """
  495. return var_operation_return(
  496. js_expression=f"{object}.hasOwnProperty({key})",
  497. var_type=bool,
  498. )