object.py 15 KB

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