object.py 14 KB

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