object.py 17 KB

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