object.py 17 KB

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