object.py 15 KB

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