object.py 17 KB

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