object.py 14 KB

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