object.py 15 KB

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