base.py 60 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912
  1. """Collection of base classes."""
  2. from __future__ import annotations
  3. import contextlib
  4. import dataclasses
  5. import datetime
  6. import dis
  7. import functools
  8. import inspect
  9. import json
  10. import sys
  11. import warnings
  12. from types import CodeType, FunctionType
  13. from typing import (
  14. TYPE_CHECKING,
  15. Any,
  16. Callable,
  17. Dict,
  18. Generic,
  19. List,
  20. Literal,
  21. NoReturn,
  22. Optional,
  23. Sequence,
  24. Set,
  25. Tuple,
  26. Type,
  27. TypeVar,
  28. Union,
  29. cast,
  30. get_args,
  31. overload,
  32. )
  33. from typing_extensions import ParamSpec, get_type_hints, override
  34. from reflex import constants
  35. from reflex.base import Base
  36. from reflex.constants.colors import Color
  37. from reflex.utils import console, imports, serializers, types
  38. from reflex.utils.exceptions import VarDependencyError, VarTypeError, VarValueError
  39. from reflex.utils.format import format_state_name
  40. from reflex.utils.types import get_origin
  41. from reflex.vars import (
  42. ComputedVar,
  43. ImmutableVarData,
  44. Var,
  45. VarData,
  46. _decode_var_immutable,
  47. _extract_var_data,
  48. _global_vars,
  49. )
  50. if TYPE_CHECKING:
  51. from reflex.state import BaseState
  52. from .function import FunctionVar, ToFunctionOperation
  53. from .number import (
  54. BooleanVar,
  55. NumberVar,
  56. ToBooleanVarOperation,
  57. ToNumberVarOperation,
  58. )
  59. from .object import ObjectVar, ToObjectOperation
  60. from .sequence import ArrayVar, StringVar, ToArrayOperation, ToStringOperation
  61. VAR_TYPE = TypeVar("VAR_TYPE")
  62. @dataclasses.dataclass(
  63. eq=False,
  64. frozen=True,
  65. **{"slots": True} if sys.version_info >= (3, 10) else {},
  66. )
  67. class ImmutableVar(Var, Generic[VAR_TYPE]):
  68. """Base class for immutable vars."""
  69. # The name of the var.
  70. _var_name: str = dataclasses.field()
  71. # The type of the var.
  72. _var_type: types.GenericType = dataclasses.field(default=Any)
  73. # Extra metadata associated with the Var
  74. _var_data: Optional[ImmutableVarData] = dataclasses.field(default=None)
  75. def __str__(self) -> str:
  76. """String representation of the var. Guaranteed to be a valid Javascript expression.
  77. Returns:
  78. The name of the var.
  79. """
  80. return self._var_name
  81. @property
  82. def _var_is_local(self) -> bool:
  83. """Whether this is a local javascript variable.
  84. Returns:
  85. False
  86. """
  87. return False
  88. @property
  89. def _var_is_string(self) -> bool:
  90. """Whether the var is a string literal.
  91. Returns:
  92. False
  93. """
  94. return False
  95. @property
  96. def _var_full_name_needs_state_prefix(self) -> bool:
  97. """Whether the full name of the var needs a _var_state prefix.
  98. Returns:
  99. False
  100. """
  101. return False
  102. def __post_init__(self):
  103. """Post-initialize the var."""
  104. # Decode any inline Var markup and apply it to the instance
  105. _var_data, _var_name = _decode_var_immutable(self._var_name)
  106. if _var_data or _var_name != self._var_name:
  107. self.__init__(
  108. _var_name=_var_name,
  109. _var_type=self._var_type,
  110. _var_data=ImmutableVarData.merge(self._var_data, _var_data),
  111. )
  112. def __hash__(self) -> int:
  113. """Define a hash function for the var.
  114. Returns:
  115. The hash of the var.
  116. """
  117. return hash((self._var_name, self._var_type, self._var_data))
  118. def _get_all_var_data(self) -> ImmutableVarData | None:
  119. """Get all VarData associated with the Var.
  120. Returns:
  121. The VarData of the components and all of its children.
  122. """
  123. return self._var_data
  124. def _replace(self, merge_var_data=None, **kwargs: Any):
  125. """Make a copy of this Var with updated fields.
  126. Args:
  127. merge_var_data: VarData to merge into the existing VarData.
  128. **kwargs: Var fields to update.
  129. Returns:
  130. A new ImmutableVar with the updated fields overwriting the corresponding fields in this Var.
  131. Raises:
  132. TypeError: If _var_is_local, _var_is_string, or _var_full_name_needs_state_prefix is not None.
  133. """
  134. if kwargs.get("_var_is_local", False) is not False:
  135. raise TypeError(
  136. "The _var_is_local argument is not supported for ImmutableVar."
  137. )
  138. if kwargs.get("_var_is_string", False) is not False:
  139. raise TypeError(
  140. "The _var_is_string argument is not supported for ImmutableVar."
  141. )
  142. if kwargs.get("_var_full_name_needs_state_prefix", False) is not False:
  143. raise TypeError(
  144. "The _var_full_name_needs_state_prefix argument is not supported for ImmutableVar."
  145. )
  146. return dataclasses.replace(
  147. self,
  148. _var_data=ImmutableVarData.merge(
  149. kwargs.get("_var_data", self._var_data), merge_var_data
  150. ),
  151. **kwargs,
  152. )
  153. @classmethod
  154. def create(
  155. cls,
  156. value: Any,
  157. _var_is_local: bool | None = None,
  158. _var_is_string: bool | None = None,
  159. _var_data: VarData | None = None,
  160. ) -> ImmutableVar | Var | None:
  161. """Create a var from a value.
  162. Args:
  163. value: The value to create the var from.
  164. _var_is_local: Whether the var is local. Deprecated.
  165. _var_is_string: Whether the var is a string literal. Deprecated.
  166. _var_data: Additional hooks and imports associated with the Var.
  167. Returns:
  168. The var.
  169. Raises:
  170. VarTypeError: If the value is JSON-unserializable.
  171. TypeError: If _var_is_local or _var_is_string is not None.
  172. """
  173. if _var_is_local is not None:
  174. raise TypeError(
  175. "The _var_is_local argument is not supported for ImmutableVar."
  176. )
  177. if _var_is_string is not None:
  178. raise TypeError(
  179. "The _var_is_string argument is not supported for ImmutableVar."
  180. )
  181. from reflex.utils import format
  182. # Check for none values.
  183. if value is None:
  184. return None
  185. # If the value is already a var, do nothing.
  186. if isinstance(value, Var):
  187. return value
  188. # Try to pull the imports and hooks from contained values.
  189. if not isinstance(value, str):
  190. _var_data = VarData.merge(*_extract_var_data(value), _var_data)
  191. # Try to serialize the value.
  192. type_ = type(value)
  193. if type_ in types.JSONType:
  194. name = value
  195. else:
  196. name, _serialized_type = serializers.serialize(value, get_type=True)
  197. if name is None:
  198. raise VarTypeError(
  199. f"No JSON serializer found for var {value} of type {type_}."
  200. )
  201. name = name if isinstance(name, str) else format.json_dumps(name)
  202. return cls(
  203. _var_name=name,
  204. _var_type=type_,
  205. _var_data=(
  206. ImmutableVarData(
  207. state=_var_data.state,
  208. imports=_var_data.imports,
  209. hooks=_var_data.hooks,
  210. )
  211. if _var_data
  212. else None
  213. ),
  214. )
  215. @classmethod
  216. def create_safe(
  217. cls,
  218. value: Any,
  219. _var_is_local: bool | None = None,
  220. _var_is_string: bool | None = None,
  221. _var_data: VarData | None = None,
  222. ) -> Var | ImmutableVar:
  223. """Create a var from a value, asserting that it is not None.
  224. Args:
  225. value: The value to create the var from.
  226. _var_is_local: Whether the var is local. Deprecated.
  227. _var_is_string: Whether the var is a string literal. Deprecated.
  228. _var_data: Additional hooks and imports associated with the Var.
  229. Returns:
  230. The var.
  231. """
  232. var = cls.create(
  233. value,
  234. _var_is_local=_var_is_local,
  235. _var_is_string=_var_is_string,
  236. _var_data=_var_data,
  237. )
  238. assert var is not None
  239. return var
  240. def __format__(self, format_spec: str) -> str:
  241. """Format the var into a Javascript equivalent to an f-string.
  242. Args:
  243. format_spec: The format specifier (Ignored for now).
  244. Returns:
  245. The formatted var.
  246. """
  247. hashed_var = hash(self)
  248. _global_vars[hashed_var] = self
  249. # Encode the _var_data into the formatted output for tracking purposes.
  250. return f"{constants.REFLEX_VAR_OPENING_TAG}{hashed_var}{constants.REFLEX_VAR_CLOSING_TAG}{self._var_name}"
  251. @overload
  252. def to(
  253. self, output: Type[NumberVar], var_type: type[int] | type[float] = float
  254. ) -> ToNumberVarOperation: ...
  255. @overload
  256. def to(self, output: Type[BooleanVar]) -> ToBooleanVarOperation: ...
  257. @overload
  258. def to(
  259. self,
  260. output: Type[ArrayVar],
  261. var_type: type[list] | type[tuple] | type[set] = list,
  262. ) -> ToArrayOperation: ...
  263. @overload
  264. def to(self, output: Type[StringVar]) -> ToStringOperation: ...
  265. @overload
  266. def to(
  267. self, output: Type[ObjectVar], var_type: types.GenericType = dict
  268. ) -> ToObjectOperation: ...
  269. @overload
  270. def to(
  271. self, output: Type[FunctionVar], var_type: Type[Callable] = Callable
  272. ) -> ToFunctionOperation: ...
  273. @overload
  274. def to(
  275. self,
  276. output: Type[OUTPUT] | types.GenericType,
  277. var_type: types.GenericType | None = None,
  278. ) -> OUTPUT: ...
  279. def to(
  280. self,
  281. output: Type[OUTPUT] | types.GenericType,
  282. var_type: types.GenericType | None = None,
  283. ) -> Var:
  284. """Convert the var to a different type.
  285. Args:
  286. output: The output type.
  287. var_type: The type of the var.
  288. Raises:
  289. TypeError: If the var_type is not a supported type for the output.
  290. Returns:
  291. The converted var.
  292. """
  293. from .function import FunctionVar, ToFunctionOperation
  294. from .number import (
  295. BooleanVar,
  296. NumberVar,
  297. ToBooleanVarOperation,
  298. ToNumberVarOperation,
  299. )
  300. from .object import ObjectVar, ToObjectOperation
  301. from .sequence import ArrayVar, StringVar, ToArrayOperation, ToStringOperation
  302. base_type = var_type
  303. if types.is_optional(base_type):
  304. base_type = types.get_args(base_type)[0]
  305. fixed_type = get_origin(base_type) or base_type
  306. fixed_output_type = get_origin(output) or output
  307. # If the first argument is a python type, we map it to the corresponding Var type.
  308. if fixed_output_type is dict:
  309. return self.to(ObjectVar, output)
  310. if fixed_output_type in (list, tuple, set):
  311. return self.to(ArrayVar, output)
  312. if fixed_output_type in (int, float):
  313. return self.to(NumberVar, output)
  314. if fixed_output_type is str:
  315. return self.to(StringVar, output)
  316. if fixed_output_type is bool:
  317. return self.to(BooleanVar, output)
  318. if issubclass(output, NumberVar):
  319. if fixed_type is not None:
  320. if fixed_type is Union:
  321. inner_types = get_args(base_type)
  322. if not all(issubclass(t, (int, float)) for t in inner_types):
  323. raise TypeError(
  324. f"Unsupported type {var_type} for NumberVar. Must be int or float."
  325. )
  326. elif not issubclass(fixed_type, (int, float)):
  327. raise TypeError(
  328. f"Unsupported type {var_type} for NumberVar. Must be int or float."
  329. )
  330. return ToNumberVarOperation.create(self, var_type or float)
  331. if issubclass(output, BooleanVar):
  332. return ToBooleanVarOperation.create(self)
  333. if issubclass(output, ArrayVar):
  334. if fixed_type is not None and not issubclass(
  335. fixed_type, (list, tuple, set)
  336. ):
  337. raise TypeError(
  338. f"Unsupported type {var_type} for ArrayVar. Must be list, tuple, or set."
  339. )
  340. return ToArrayOperation.create(self, var_type or list)
  341. if issubclass(output, StringVar):
  342. return ToStringOperation.create(self)
  343. if issubclass(output, (ObjectVar, Base)):
  344. return ToObjectOperation.create(self, var_type or dict)
  345. if issubclass(output, FunctionVar):
  346. # if fixed_type is not None and not issubclass(fixed_type, Callable):
  347. # raise TypeError(
  348. # f"Unsupported type {var_type} for FunctionVar. Must be Callable."
  349. # )
  350. return ToFunctionOperation.create(self, var_type or Callable)
  351. # If we can't determine the first argument, we just replace the _var_type.
  352. if not issubclass(output, Var) or var_type is None:
  353. return dataclasses.replace(
  354. self,
  355. _var_type=output,
  356. )
  357. # We couldn't determine the output type to be any other Var type, so we replace the _var_type.
  358. if var_type is not None:
  359. return dataclasses.replace(
  360. self,
  361. _var_type=var_type,
  362. )
  363. return self
  364. def guess_type(self) -> ImmutableVar:
  365. """Guesses the type of the variable based on its `_var_type` attribute.
  366. Returns:
  367. ImmutableVar: The guessed type of the variable.
  368. Raises:
  369. TypeError: If the type is not supported for guessing.
  370. """
  371. from .number import BooleanVar, NumberVar
  372. from .object import ObjectVar
  373. from .sequence import ArrayVar, StringVar
  374. var_type = self._var_type
  375. if types.is_optional(var_type):
  376. var_type = types.get_args(var_type)[0]
  377. if var_type is Any:
  378. return self
  379. fixed_type = get_origin(var_type) or var_type
  380. if fixed_type is Union:
  381. inner_types = get_args(var_type)
  382. if int in inner_types and float in inner_types:
  383. return self.to(NumberVar, self._var_type)
  384. return self
  385. if not inspect.isclass(fixed_type):
  386. raise TypeError(f"Unsupported type {var_type} for guess_type.")
  387. if issubclass(fixed_type, bool):
  388. return self.to(BooleanVar, self._var_type)
  389. if issubclass(fixed_type, (int, float)):
  390. return self.to(NumberVar, self._var_type)
  391. if issubclass(fixed_type, dict):
  392. return self.to(ObjectVar, self._var_type)
  393. if issubclass(fixed_type, (list, tuple, set)):
  394. return self.to(ArrayVar, self._var_type)
  395. if issubclass(fixed_type, str):
  396. return self.to(StringVar)
  397. if issubclass(fixed_type, Base):
  398. return self.to(ObjectVar, self._var_type)
  399. return self
  400. def get_default_value(self) -> Any:
  401. """Get the default value of the var.
  402. Returns:
  403. The default value of the var.
  404. Raises:
  405. ImportError: If the var is a dataframe and pandas is not installed.
  406. """
  407. if types.is_optional(self._var_type):
  408. return None
  409. type_ = (
  410. get_origin(self._var_type)
  411. if types.is_generic_alias(self._var_type)
  412. else self._var_type
  413. )
  414. if type_ is Literal:
  415. args = get_args(self._var_type)
  416. return args[0] if args else None
  417. if issubclass(type_, str):
  418. return ""
  419. if issubclass(type_, types.get_args(Union[int, float])):
  420. return 0
  421. if issubclass(type_, bool):
  422. return False
  423. if issubclass(type_, list):
  424. return []
  425. if issubclass(type_, dict):
  426. return {}
  427. if issubclass(type_, tuple):
  428. return ()
  429. if types.is_dataframe(type_):
  430. try:
  431. import pandas as pd
  432. return pd.DataFrame()
  433. except ImportError as e:
  434. raise ImportError(
  435. "Please install pandas to use dataframes in your app."
  436. ) from e
  437. return set() if issubclass(type_, set) else None
  438. def get_setter_name(self, include_state: bool = True) -> str:
  439. """Get the name of the var's generated setter function.
  440. Args:
  441. include_state: Whether to include the state name in the setter name.
  442. Returns:
  443. The name of the setter function.
  444. """
  445. var_name_parts = self._var_name.split(".")
  446. setter = constants.SETTER_PREFIX + var_name_parts[-1]
  447. if self._var_data is None:
  448. return setter
  449. if not include_state or self._var_data.state == "":
  450. return setter
  451. return ".".join((self._var_data.state, setter))
  452. def get_setter(self) -> Callable[[BaseState, Any], None]:
  453. """Get the var's setter function.
  454. Returns:
  455. A function that that creates a setter for the var.
  456. """
  457. actual_name = self._var_name.split(".")[-1]
  458. def setter(state: BaseState, value: Any):
  459. """Get the setter for the var.
  460. Args:
  461. state: The state within which we add the setter function.
  462. value: The value to set.
  463. """
  464. if self._var_type in [int, float]:
  465. try:
  466. value = self._var_type(value)
  467. setattr(state, actual_name, value)
  468. except ValueError:
  469. console.debug(
  470. f"{type(state).__name__}.{self._var_name}: Failed conversion of {value} to '{self._var_type.__name__}'. Value not set.",
  471. )
  472. else:
  473. setattr(state, actual_name, value)
  474. setter.__qualname__ = self.get_setter_name()
  475. return setter
  476. def __eq__(self, other: Var | Any) -> BooleanVar:
  477. """Check if the current variable is equal to the given variable.
  478. Args:
  479. other (Var | Any): The variable to compare with.
  480. Returns:
  481. BooleanVar: A BooleanVar object representing the result of the equality check.
  482. """
  483. from .number import equal_operation
  484. return equal_operation(self, other)
  485. def __ne__(self, other: Var | Any) -> BooleanVar:
  486. """Check if the current object is not equal to the given object.
  487. Parameters:
  488. other (Var | Any): The object to compare with.
  489. Returns:
  490. BooleanVar: A BooleanVar object representing the result of the comparison.
  491. """
  492. from .number import equal_operation
  493. return ~equal_operation(self, other)
  494. def __gt__(self, other: Var | Any) -> BooleanVar:
  495. """Compare the current instance with another variable and return a BooleanVar representing the result of the greater than operation.
  496. Args:
  497. other (Var | Any): The variable to compare with.
  498. Returns:
  499. BooleanVar: A BooleanVar representing the result of the greater than operation.
  500. """
  501. from .number import greater_than_operation
  502. return greater_than_operation(self, other)
  503. def __ge__(self, other: Var | Any) -> BooleanVar:
  504. """Check if the value of this variable is greater than or equal to the value of another variable or object.
  505. Args:
  506. other (Var | Any): The variable or object to compare with.
  507. Returns:
  508. BooleanVar: A BooleanVar object representing the result of the comparison.
  509. """
  510. from .number import greater_than_or_equal_operation
  511. return greater_than_or_equal_operation(self, other)
  512. def __lt__(self, other: Var | Any) -> BooleanVar:
  513. """Compare the current instance with another variable using the less than (<) operator.
  514. Args:
  515. other: The variable to compare with.
  516. Returns:
  517. A `BooleanVar` object representing the result of the comparison.
  518. """
  519. from .number import less_than_operation
  520. return less_than_operation(self, other)
  521. def __le__(self, other: Var | Any) -> BooleanVar:
  522. """Compare if the current instance is less than or equal to the given value.
  523. Args:
  524. other: The value to compare with.
  525. Returns:
  526. A BooleanVar object representing the result of the comparison.
  527. """
  528. from .number import less_than_or_equal_operation
  529. return less_than_or_equal_operation(self, other)
  530. def bool(self) -> BooleanVar:
  531. """Convert the var to a boolean.
  532. Returns:
  533. The boolean var.
  534. """
  535. from .number import boolify
  536. return boolify(self)
  537. def __and__(self, other: Var | Any) -> ImmutableVar:
  538. """Perform a logical AND operation on the current instance and another variable.
  539. Args:
  540. other: The variable to perform the logical AND operation with.
  541. Returns:
  542. A `BooleanVar` object representing the result of the logical AND operation.
  543. """
  544. return and_operation(self, other)
  545. def __rand__(self, other: Var | Any) -> ImmutableVar:
  546. """Perform a logical AND operation on the current instance and another variable.
  547. Args:
  548. other: The variable to perform the logical AND operation with.
  549. Returns:
  550. A `BooleanVar` object representing the result of the logical AND operation.
  551. """
  552. return and_operation(other, self)
  553. def __or__(self, other: Var | Any) -> ImmutableVar:
  554. """Perform a logical OR operation on the current instance and another variable.
  555. Args:
  556. other: The variable to perform the logical OR operation with.
  557. Returns:
  558. A `BooleanVar` object representing the result of the logical OR operation.
  559. """
  560. return or_operation(self, other)
  561. def __ror__(self, other: Var | Any) -> ImmutableVar:
  562. """Perform a logical OR operation on the current instance and another variable.
  563. Args:
  564. other: The variable to perform the logical OR operation with.
  565. Returns:
  566. A `BooleanVar` object representing the result of the logical OR operation.
  567. """
  568. return or_operation(other, self)
  569. def __invert__(self) -> BooleanVar:
  570. """Perform a logical NOT operation on the current instance.
  571. Returns:
  572. A `BooleanVar` object representing the result of the logical NOT operation.
  573. """
  574. return ~self.bool()
  575. def to_string(self) -> ImmutableVar:
  576. """Convert the var to a string.
  577. Returns:
  578. The string var.
  579. """
  580. from .function import JSON_STRINGIFY
  581. from .sequence import StringVar
  582. return JSON_STRINGIFY.call(self).to(StringVar)
  583. def as_ref(self) -> ImmutableVar:
  584. """Get a reference to the var.
  585. Returns:
  586. The reference to the var.
  587. """
  588. from .object import ObjectVar
  589. refs = ImmutableVar(
  590. _var_name="refs",
  591. _var_data=ImmutableVarData(
  592. imports={
  593. f"/{constants.Dirs.STATE_PATH}": [imports.ImportVar(tag="refs")]
  594. }
  595. ),
  596. ).to(ObjectVar)
  597. return refs[LiteralVar.create(str(self))]
  598. def _type(self) -> StringVar:
  599. """Returns the type of the object.
  600. This method uses the `typeof` function from the `FunctionStringVar` class
  601. to determine the type of the object.
  602. Returns:
  603. StringVar: A string variable representing the type of the object.
  604. """
  605. from .function import FunctionStringVar
  606. from .sequence import StringVar
  607. type_of = FunctionStringVar("typeof")
  608. return type_of.call(self).to(StringVar)
  609. OUTPUT = TypeVar("OUTPUT", bound=ImmutableVar)
  610. class LiteralVar(ImmutableVar):
  611. """Base class for immutable literal vars."""
  612. @classmethod
  613. def create(
  614. cls,
  615. value: Any,
  616. _var_data: VarData | None = None,
  617. ) -> Var:
  618. """Create a var from a value.
  619. Args:
  620. value: The value to create the var from.
  621. _var_data: Additional hooks and imports associated with the Var.
  622. Returns:
  623. The var.
  624. Raises:
  625. TypeError: If the value is not a supported type for LiteralVar.
  626. """
  627. from .number import LiteralBooleanVar, LiteralNumberVar
  628. from .object import LiteralObjectVar
  629. from .sequence import LiteralArrayVar, LiteralStringVar
  630. if isinstance(value, Var):
  631. if _var_data is None:
  632. return value
  633. return value._replace(merge_var_data=_var_data)
  634. if isinstance(value, str):
  635. return LiteralStringVar.create(value, _var_data=_var_data)
  636. if isinstance(value, bool):
  637. return LiteralBooleanVar.create(value, _var_data=_var_data)
  638. if isinstance(value, (int, float)):
  639. return LiteralNumberVar.create(value, _var_data=_var_data)
  640. if isinstance(value, dict):
  641. return LiteralObjectVar.create(value, _var_data=_var_data)
  642. if isinstance(value, Color):
  643. return LiteralStringVar.create(f"{value}", _var_data=_var_data)
  644. if isinstance(value, (list, tuple, set)):
  645. return LiteralArrayVar.create(value, _var_data=_var_data)
  646. if value is None:
  647. return ImmutableVar.create_safe("null", _var_data=_var_data)
  648. from reflex.event import EventChain, EventSpec
  649. from reflex.utils.format import get_event_handler_parts
  650. from .function import ArgsFunctionOperation, FunctionStringVar
  651. from .object import LiteralObjectVar
  652. if isinstance(value, EventSpec):
  653. event_name = LiteralVar.create(
  654. ".".join(filter(None, get_event_handler_parts(value.handler)))
  655. )
  656. event_args = LiteralVar.create(
  657. {str(name): value for name, value in value.args}
  658. )
  659. event_client_name = LiteralVar.create(value.client_handler_name)
  660. return FunctionStringVar("Event").call(
  661. event_name,
  662. event_args,
  663. *([event_client_name] if value.client_handler_name else []),
  664. )
  665. if isinstance(value, EventChain):
  666. sig = inspect.signature(value.args_spec) # type: ignore
  667. if sig.parameters:
  668. arg_def = tuple((f"_{p}" for p in sig.parameters))
  669. arg_def_expr = LiteralVar.create(
  670. [ImmutableVar.create_safe(arg) for arg in arg_def]
  671. )
  672. else:
  673. # add a default argument for addEvents if none were specified in value.args_spec
  674. # used to trigger the preventDefault() on the event.
  675. arg_def = ("...args",)
  676. arg_def_expr = ImmutableVar.create_safe("args")
  677. return ArgsFunctionOperation.create(
  678. arg_def,
  679. FunctionStringVar.create("addEvents").call(
  680. LiteralVar.create(
  681. [LiteralVar.create(event) for event in value.events]
  682. ),
  683. arg_def_expr,
  684. LiteralVar.create(value.event_actions),
  685. ),
  686. )
  687. try:
  688. from plotly.graph_objects import Figure, layout
  689. from plotly.io import to_json
  690. if isinstance(value, Figure):
  691. return LiteralObjectVar.create(
  692. json.loads(str(to_json(value))),
  693. _var_type=Figure,
  694. _var_data=_var_data,
  695. )
  696. if isinstance(value, layout.Template):
  697. return LiteralObjectVar.create(
  698. {
  699. "data": json.loads(str(to_json(value.data))),
  700. "layout": json.loads(str(to_json(value.layout))),
  701. },
  702. _var_type=layout.Template,
  703. _var_data=_var_data,
  704. )
  705. except ImportError:
  706. pass
  707. try:
  708. import base64
  709. import io
  710. from PIL.Image import MIME
  711. from PIL.Image import Image as Img
  712. if isinstance(value, Img):
  713. with io.BytesIO() as buffer:
  714. image_format = getattr(value, "format", None) or "PNG"
  715. value.save(buffer, format=image_format)
  716. try:
  717. # Newer method to get the mime type, but does not always work.
  718. mimetype = value.get_format_mimetype() # type: ignore
  719. except AttributeError:
  720. try:
  721. # Fallback method
  722. mimetype = MIME[image_format]
  723. except KeyError:
  724. # Unknown mime_type: warn and return image/png and hope the browser can sort it out.
  725. warnings.warn( # noqa: B028
  726. f"Unknown mime type for {value} {image_format}. Defaulting to image/png"
  727. )
  728. mimetype = "image/png"
  729. return LiteralStringVar.create(
  730. f"data:{mimetype};base64,{base64.b64encode(buffer.getvalue()).decode()}",
  731. _var_data=_var_data,
  732. )
  733. except ImportError:
  734. pass
  735. if isinstance(value, Base):
  736. return LiteralObjectVar.create(
  737. {k: (None if callable(v) else v) for k, v in value.dict().items()},
  738. _var_type=type(value),
  739. _var_data=_var_data,
  740. )
  741. raise TypeError(
  742. f"Unsupported type {type(value)} for LiteralVar. Tried to create a LiteralVar from {value}."
  743. )
  744. def __post_init__(self):
  745. """Post-initialize the var."""
  746. def json(self) -> str:
  747. """Serialize the var to a JSON string.
  748. Raises:
  749. NotImplementedError: If the method is not implemented.
  750. """
  751. raise NotImplementedError(
  752. "LiteralVar subclasses must implement the json method."
  753. )
  754. P = ParamSpec("P")
  755. T = TypeVar("T")
  756. # NoReturn is used to match CustomVarOperationReturn with no type hint.
  757. @overload
  758. def var_operation(
  759. func: Callable[P, CustomVarOperationReturn[NoReturn]],
  760. ) -> Callable[P, ImmutableVar]: ...
  761. @overload
  762. def var_operation(
  763. func: Callable[P, CustomVarOperationReturn[bool]],
  764. ) -> Callable[P, BooleanVar]: ...
  765. NUMBER_T = TypeVar("NUMBER_T", int, float, Union[int, float])
  766. @overload
  767. def var_operation(
  768. func: Callable[P, CustomVarOperationReturn[NUMBER_T]],
  769. ) -> Callable[P, NumberVar[NUMBER_T]]: ...
  770. @overload
  771. def var_operation(
  772. func: Callable[P, CustomVarOperationReturn[str]],
  773. ) -> Callable[P, StringVar]: ...
  774. LIST_T = TypeVar("LIST_T", bound=Union[List[Any], Tuple, Set])
  775. @overload
  776. def var_operation(
  777. func: Callable[P, CustomVarOperationReturn[LIST_T]],
  778. ) -> Callable[P, ArrayVar[LIST_T]]: ...
  779. OBJECT_TYPE = TypeVar("OBJECT_TYPE", bound=Dict)
  780. @overload
  781. def var_operation(
  782. func: Callable[P, CustomVarOperationReturn[OBJECT_TYPE]],
  783. ) -> Callable[P, ObjectVar[OBJECT_TYPE]]: ...
  784. def var_operation(
  785. func: Callable[P, CustomVarOperationReturn[T]],
  786. ) -> Callable[P, ImmutableVar[T]]:
  787. """Decorator for creating a var operation.
  788. Example:
  789. ```python
  790. @var_operation
  791. def add(a: NumberVar, b: NumberVar):
  792. return custom_var_operation(f"{a} + {b}")
  793. ```
  794. Args:
  795. func: The function to decorate.
  796. Returns:
  797. The decorated function.
  798. """
  799. @functools.wraps(func)
  800. def wrapper(*args: P.args, **kwargs: P.kwargs) -> ImmutableVar[T]:
  801. func_args = list(inspect.signature(func).parameters)
  802. args_vars = {
  803. func_args[i]: (LiteralVar.create(arg) if not isinstance(arg, Var) else arg)
  804. for i, arg in enumerate(args)
  805. }
  806. kwargs_vars = {
  807. key: LiteralVar.create(value) if not isinstance(value, Var) else value
  808. for key, value in kwargs.items()
  809. }
  810. return CustomVarOperation.create(
  811. args=tuple(list(args_vars.items()) + list(kwargs_vars.items())),
  812. return_var=func(*args_vars.values(), **kwargs_vars), # type: ignore
  813. ).guess_type()
  814. return wrapper
  815. def unionize(*args: Type) -> Type:
  816. """Unionize the types.
  817. Args:
  818. args: The types to unionize.
  819. Returns:
  820. The unionized types.
  821. """
  822. if not args:
  823. return Any
  824. first, *rest = args
  825. if not rest:
  826. return first
  827. return Union[first, unionize(*rest)]
  828. def figure_out_type(value: Any) -> types.GenericType:
  829. """Figure out the type of the value.
  830. Args:
  831. value: The value to figure out the type of.
  832. Returns:
  833. The type of the value.
  834. """
  835. if isinstance(value, list):
  836. return List[unionize(*(figure_out_type(v) for v in value))]
  837. if isinstance(value, set):
  838. return Set[unionize(*(figure_out_type(v) for v in value))]
  839. if isinstance(value, tuple):
  840. return Tuple[unionize(*(figure_out_type(v) for v in value)), ...]
  841. if isinstance(value, dict):
  842. return Dict[
  843. unionize(*(figure_out_type(k) for k in value)),
  844. unionize(*(figure_out_type(v) for v in value.values())),
  845. ]
  846. if isinstance(value, Var):
  847. return value._var_type
  848. return type(value)
  849. class cached_property_no_lock(functools.cached_property):
  850. """A special version of functools.cached_property that does not use a lock."""
  851. def __init__(self, func):
  852. """Initialize the cached_property_no_lock.
  853. Args:
  854. func: The function to cache.
  855. """
  856. super().__init__(func)
  857. self.lock = contextlib.nullcontext()
  858. class CachedVarOperation:
  859. """Base class for cached var operations to lower boilerplate code."""
  860. def __post_init__(self):
  861. """Post-initialize the CachedVarOperation."""
  862. object.__delattr__(self, "_var_name")
  863. def __getattr__(self, name: str) -> Any:
  864. """Get an attribute of the var.
  865. Args:
  866. name: The name of the attribute.
  867. Returns:
  868. The attribute.
  869. """
  870. if name == "_var_name":
  871. return self._cached_var_name
  872. parent_classes = inspect.getmro(self.__class__)
  873. return parent_classes[parent_classes.index(CachedVarOperation) + 1].__getattr__( # type: ignore
  874. self, name
  875. )
  876. def _get_all_var_data(self) -> ImmutableVarData | None:
  877. """Get all VarData associated with the Var.
  878. Returns:
  879. The VarData of the components and all of its children.
  880. """
  881. return self._cached_get_all_var_data
  882. @cached_property_no_lock
  883. def _cached_get_all_var_data(self) -> ImmutableVarData | None:
  884. """Get the cached VarData.
  885. Returns:
  886. The cached VarData.
  887. """
  888. return ImmutableVarData.merge(
  889. *map(
  890. lambda value: (
  891. value._get_all_var_data() if isinstance(value, Var) else None
  892. ),
  893. map(
  894. lambda field: getattr(self, field.name),
  895. dataclasses.fields(self), # type: ignore
  896. ),
  897. ),
  898. self._var_data,
  899. )
  900. def __hash__(self) -> int:
  901. """Calculate the hash of the object.
  902. Returns:
  903. The hash of the object.
  904. """
  905. return hash(
  906. (
  907. self.__class__.__name__,
  908. *[
  909. getattr(self, field.name)
  910. for field in dataclasses.fields(self) # type: ignore
  911. if field.name not in ["_var_name", "_var_data", "_var_type"]
  912. ],
  913. )
  914. )
  915. def and_operation(a: Var | Any, b: Var | Any) -> ImmutableVar:
  916. """Perform a logical AND operation on two variables.
  917. Args:
  918. a: The first variable.
  919. b: The second variable.
  920. Returns:
  921. The result of the logical AND operation.
  922. """
  923. return _and_operation(a, b) # type: ignore
  924. @var_operation
  925. def _and_operation(a: ImmutableVar, b: ImmutableVar):
  926. """Perform a logical AND operation on two variables.
  927. Args:
  928. a: The first variable.
  929. b: The second variable.
  930. Returns:
  931. The result of the logical AND operation.
  932. """
  933. return var_operation_return(
  934. js_expression=f"({a} && {b})",
  935. var_type=unionize(a._var_type, b._var_type),
  936. )
  937. def or_operation(a: Var | Any, b: Var | Any) -> ImmutableVar:
  938. """Perform a logical OR operation on two variables.
  939. Args:
  940. a: The first variable.
  941. b: The second variable.
  942. Returns:
  943. The result of the logical OR operation.
  944. """
  945. return _or_operation(a, b) # type: ignore
  946. @var_operation
  947. def _or_operation(a: ImmutableVar, b: ImmutableVar):
  948. """Perform a logical OR operation on two variables.
  949. Args:
  950. a: The first variable.
  951. b: The second variable.
  952. Returns:
  953. The result of the logical OR operation.
  954. """
  955. return var_operation_return(
  956. js_expression=f"({a} || {b})",
  957. var_type=unionize(a._var_type, b._var_type),
  958. )
  959. @dataclasses.dataclass(
  960. eq=False,
  961. frozen=True,
  962. **{"slots": True} if sys.version_info >= (3, 10) else {},
  963. )
  964. class ImmutableCallableVar(ImmutableVar):
  965. """Decorate a Var-returning function to act as both a Var and a function.
  966. This is used as a compatibility shim for replacing Var objects in the
  967. API with functions that return a family of Var.
  968. """
  969. fn: Callable[..., Var] = dataclasses.field(
  970. default_factory=lambda: lambda: ImmutableVar(_var_name="undefined")
  971. )
  972. original_var: Var = dataclasses.field(
  973. default_factory=lambda: ImmutableVar(_var_name="undefined")
  974. )
  975. def __init__(self, fn: Callable[..., Var]):
  976. """Initialize a CallableVar.
  977. Args:
  978. fn: The function to decorate (must return Var)
  979. """
  980. original_var = fn()
  981. super(ImmutableCallableVar, self).__init__(
  982. _var_name=original_var._var_name,
  983. _var_type=original_var._var_type,
  984. _var_data=ImmutableVarData.merge(original_var._get_all_var_data()),
  985. )
  986. object.__setattr__(self, "fn", fn)
  987. object.__setattr__(self, "original_var", original_var)
  988. def __call__(self, *args, **kwargs) -> Var:
  989. """Call the decorated function.
  990. Args:
  991. *args: The args to pass to the function.
  992. **kwargs: The kwargs to pass to the function.
  993. Returns:
  994. The Var returned from calling the function.
  995. """
  996. return self.fn(*args, **kwargs)
  997. def __hash__(self) -> int:
  998. """Calculate the hash of the object.
  999. Returns:
  1000. The hash of the object.
  1001. """
  1002. return hash((self.__class__.__name__, self.original_var))
  1003. RETURN_TYPE = TypeVar("RETURN_TYPE")
  1004. DICT_KEY = TypeVar("DICT_KEY")
  1005. DICT_VAL = TypeVar("DICT_VAL")
  1006. LIST_INSIDE = TypeVar("LIST_INSIDE")
  1007. @dataclasses.dataclass(
  1008. eq=False,
  1009. frozen=True,
  1010. **{"slots": True} if sys.version_info >= (3, 10) else {},
  1011. )
  1012. class ImmutableComputedVar(ImmutableVar[RETURN_TYPE]):
  1013. """A field with computed getters."""
  1014. # Whether to track dependencies and cache computed values
  1015. _cache: bool = dataclasses.field(default=False)
  1016. # Whether the computed var is a backend var
  1017. _backend: bool = dataclasses.field(default=False)
  1018. # The initial value of the computed var
  1019. _initial_value: RETURN_TYPE | types.Unset = dataclasses.field(default=types.Unset())
  1020. # Explicit var dependencies to track
  1021. _static_deps: set[str] = dataclasses.field(default_factory=set)
  1022. # Whether var dependencies should be auto-determined
  1023. _auto_deps: bool = dataclasses.field(default=True)
  1024. # Interval at which the computed var should be updated
  1025. _update_interval: Optional[datetime.timedelta] = dataclasses.field(default=None)
  1026. _fget: Callable[[BaseState], RETURN_TYPE] = dataclasses.field(
  1027. default_factory=lambda: lambda _: None
  1028. ) # type: ignore
  1029. def __init__(
  1030. self,
  1031. fget: Callable[[BASE_STATE], RETURN_TYPE],
  1032. initial_value: RETURN_TYPE | types.Unset = types.Unset(),
  1033. cache: bool = False,
  1034. deps: Optional[List[Union[str, Var]]] = None,
  1035. auto_deps: bool = True,
  1036. interval: Optional[Union[int, datetime.timedelta]] = None,
  1037. backend: bool | None = None,
  1038. **kwargs,
  1039. ):
  1040. """Initialize a ComputedVar.
  1041. Args:
  1042. fget: The getter function.
  1043. initial_value: The initial value of the computed var.
  1044. cache: Whether to cache the computed value.
  1045. deps: Explicit var dependencies to track.
  1046. auto_deps: Whether var dependencies should be auto-determined.
  1047. interval: Interval at which the computed var should be updated.
  1048. backend: Whether the computed var is a backend var.
  1049. **kwargs: additional attributes to set on the instance
  1050. Raises:
  1051. TypeError: If the computed var dependencies are not Var instances or var names.
  1052. """
  1053. hints = get_type_hints(fget)
  1054. hint = hints.get("return", Any)
  1055. kwargs["_var_name"] = kwargs.pop("_var_name", fget.__name__)
  1056. kwargs["_var_type"] = kwargs.pop("_var_type", hint)
  1057. super(ImmutableComputedVar, self).__init__(
  1058. _var_name=kwargs.pop("_var_name"),
  1059. _var_type=kwargs.pop("_var_type"),
  1060. _var_data=ImmutableVarData.merge(kwargs.pop("_var_data", None)),
  1061. )
  1062. if backend is None:
  1063. backend = fget.__name__.startswith("_")
  1064. object.__setattr__(self, "_backend", backend)
  1065. object.__setattr__(self, "_initial_value", initial_value)
  1066. object.__setattr__(self, "_cache", cache)
  1067. if isinstance(interval, int):
  1068. interval = datetime.timedelta(seconds=interval)
  1069. object.__setattr__(self, "_update_interval", interval)
  1070. if deps is None:
  1071. deps = []
  1072. else:
  1073. for dep in deps:
  1074. if isinstance(dep, Var):
  1075. continue
  1076. if isinstance(dep, str) and dep != "":
  1077. continue
  1078. raise TypeError(
  1079. "ComputedVar dependencies must be Var instances or var names (non-empty strings)."
  1080. )
  1081. object.__setattr__(
  1082. self,
  1083. "_static_deps",
  1084. {dep._var_name if isinstance(dep, Var) else dep for dep in deps},
  1085. )
  1086. object.__setattr__(self, "_auto_deps", auto_deps)
  1087. object.__setattr__(self, "_fget", fget)
  1088. @override
  1089. def _replace(self, merge_var_data=None, **kwargs: Any) -> ImmutableComputedVar:
  1090. """Replace the attributes of the ComputedVar.
  1091. Args:
  1092. merge_var_data: VarData to merge into the existing VarData.
  1093. **kwargs: Var fields to update.
  1094. Returns:
  1095. The new ComputedVar instance.
  1096. Raises:
  1097. TypeError: If kwargs contains keys that are not allowed.
  1098. """
  1099. field_values = dict(
  1100. fget=kwargs.pop("fget", self._fget),
  1101. initial_value=kwargs.pop("initial_value", self._initial_value),
  1102. cache=kwargs.pop("cache", self._cache),
  1103. deps=kwargs.pop("deps", self._static_deps),
  1104. auto_deps=kwargs.pop("auto_deps", self._auto_deps),
  1105. interval=kwargs.pop("interval", self._update_interval),
  1106. backend=kwargs.pop("backend", self._backend),
  1107. _var_name=kwargs.pop("_var_name", self._var_name),
  1108. _var_type=kwargs.pop("_var_type", self._var_type),
  1109. _var_data=kwargs.pop(
  1110. "_var_data", VarData.merge(self._var_data, merge_var_data)
  1111. ),
  1112. )
  1113. if kwargs:
  1114. unexpected_kwargs = ", ".join(kwargs.keys())
  1115. raise TypeError(f"Unexpected keyword arguments: {unexpected_kwargs}")
  1116. return ImmutableComputedVar(**field_values)
  1117. @property
  1118. def _cache_attr(self) -> str:
  1119. """Get the attribute used to cache the value on the instance.
  1120. Returns:
  1121. An attribute name.
  1122. """
  1123. return f"__cached_{self._var_name}"
  1124. @property
  1125. def _last_updated_attr(self) -> str:
  1126. """Get the attribute used to store the last updated timestamp.
  1127. Returns:
  1128. An attribute name.
  1129. """
  1130. return f"__last_updated_{self._var_name}"
  1131. def needs_update(self, instance: BaseState) -> bool:
  1132. """Check if the computed var needs to be updated.
  1133. Args:
  1134. instance: The state instance that the computed var is attached to.
  1135. Returns:
  1136. True if the computed var needs to be updated, False otherwise.
  1137. """
  1138. if self._update_interval is None:
  1139. return False
  1140. last_updated = getattr(instance, self._last_updated_attr, None)
  1141. if last_updated is None:
  1142. return True
  1143. return datetime.datetime.now() - last_updated > self._update_interval
  1144. @overload
  1145. def __get__(
  1146. self: ImmutableComputedVar[int] | ImmutableComputedVar[float],
  1147. instance: None,
  1148. owner: Type,
  1149. ) -> NumberVar: ...
  1150. @overload
  1151. def __get__(
  1152. self: ImmutableComputedVar[str],
  1153. instance: None,
  1154. owner: Type,
  1155. ) -> StringVar: ...
  1156. @overload
  1157. def __get__(
  1158. self: ImmutableComputedVar[dict[DICT_KEY, DICT_VAL]],
  1159. instance: None,
  1160. owner: Type,
  1161. ) -> ObjectVar[dict[DICT_KEY, DICT_VAL]]: ...
  1162. @overload
  1163. def __get__(
  1164. self: ImmutableComputedVar[list[LIST_INSIDE]],
  1165. instance: None,
  1166. owner: Type,
  1167. ) -> ArrayVar[list[LIST_INSIDE]]: ...
  1168. @overload
  1169. def __get__(
  1170. self: ImmutableComputedVar[set[LIST_INSIDE]],
  1171. instance: None,
  1172. owner: Type,
  1173. ) -> ArrayVar[set[LIST_INSIDE]]: ...
  1174. @overload
  1175. def __get__(
  1176. self: ImmutableComputedVar[tuple[LIST_INSIDE, ...]],
  1177. instance: None,
  1178. owner: Type,
  1179. ) -> ArrayVar[tuple[LIST_INSIDE, ...]]: ...
  1180. @overload
  1181. def __get__(
  1182. self, instance: None, owner: Type
  1183. ) -> ImmutableComputedVar[RETURN_TYPE]: ...
  1184. @overload
  1185. def __get__(self, instance: BaseState, owner: Type) -> RETURN_TYPE: ...
  1186. def __get__(self, instance: BaseState | None, owner):
  1187. """Get the ComputedVar value.
  1188. If the value is already cached on the instance, return the cached value.
  1189. Args:
  1190. instance: the instance of the class accessing this computed var.
  1191. owner: the class that this descriptor is attached to.
  1192. Returns:
  1193. The value of the var for the given instance.
  1194. """
  1195. if instance is None:
  1196. from reflex.state import BaseState
  1197. path_to_function = self.fget.__qualname__.split(".")
  1198. class_name_where_defined = (
  1199. path_to_function[-2] if len(path_to_function) > 1 else owner.__name__
  1200. )
  1201. def contains_class_name(states: Sequence[Type]) -> bool:
  1202. return any(c.__name__ == class_name_where_defined for c in states)
  1203. def is_not_mixin(state: Type[BaseState]) -> bool:
  1204. return not state._mixin
  1205. def length_of_state(state: Type[BaseState]) -> int:
  1206. return len(inspect.getmro(state))
  1207. class_where_defined = cast(
  1208. Type[BaseState],
  1209. min(
  1210. filter(
  1211. lambda state: state.__module__ == self.fget.__module__,
  1212. filter(
  1213. is_not_mixin,
  1214. filter(
  1215. lambda state: contains_class_name(
  1216. inspect.getmro(state)
  1217. ),
  1218. inspect.getmro(owner),
  1219. ),
  1220. ),
  1221. ),
  1222. default=owner,
  1223. key=length_of_state,
  1224. ),
  1225. )
  1226. return self._replace(
  1227. _var_name=format_state_name(class_where_defined.get_full_name())
  1228. + "."
  1229. + self._var_name,
  1230. merge_var_data=ImmutableVarData.from_state(class_where_defined),
  1231. ).guess_type()
  1232. if not self._cache:
  1233. return self.fget(instance)
  1234. # handle caching
  1235. if not hasattr(instance, self._cache_attr) or self.needs_update(instance):
  1236. # Set cache attr on state instance.
  1237. setattr(instance, self._cache_attr, self.fget(instance))
  1238. # Ensure the computed var gets serialized to redis.
  1239. instance._was_touched = True
  1240. # Set the last updated timestamp on the state instance.
  1241. setattr(instance, self._last_updated_attr, datetime.datetime.now())
  1242. return getattr(instance, self._cache_attr)
  1243. def _deps(
  1244. self,
  1245. objclass: Type,
  1246. obj: FunctionType | CodeType | None = None,
  1247. self_name: Optional[str] = None,
  1248. ) -> set[str]:
  1249. """Determine var dependencies of this ComputedVar.
  1250. Save references to attributes accessed on "self". Recursively called
  1251. when the function makes a method call on "self" or define comprehensions
  1252. or nested functions that may reference "self".
  1253. Args:
  1254. objclass: the class obj this ComputedVar is attached to.
  1255. obj: the object to disassemble (defaults to the fget function).
  1256. self_name: if specified, look for this name in LOAD_FAST and LOAD_DEREF instructions.
  1257. Returns:
  1258. A set of variable names accessed by the given obj.
  1259. Raises:
  1260. VarValueError: if the function references the get_state, parent_state, or substates attributes
  1261. (cannot track deps in a related state, only implicitly via parent state).
  1262. """
  1263. if not self._auto_deps:
  1264. return self._static_deps
  1265. d = self._static_deps.copy()
  1266. if obj is None:
  1267. fget = self._fget
  1268. if fget is not None:
  1269. obj = cast(FunctionType, fget)
  1270. else:
  1271. return set()
  1272. with contextlib.suppress(AttributeError):
  1273. # unbox functools.partial
  1274. obj = cast(FunctionType, obj.func) # type: ignore
  1275. with contextlib.suppress(AttributeError):
  1276. # unbox EventHandler
  1277. obj = cast(FunctionType, obj.fn) # type: ignore
  1278. if self_name is None and isinstance(obj, FunctionType):
  1279. try:
  1280. # the first argument to the function is the name of "self" arg
  1281. self_name = obj.__code__.co_varnames[0]
  1282. except (AttributeError, IndexError):
  1283. self_name = None
  1284. if self_name is None:
  1285. # cannot reference attributes on self if method takes no args
  1286. return set()
  1287. invalid_names = ["get_state", "parent_state", "substates", "get_substate"]
  1288. self_is_top_of_stack = False
  1289. for instruction in dis.get_instructions(obj):
  1290. if (
  1291. instruction.opname in ("LOAD_FAST", "LOAD_DEREF")
  1292. and instruction.argval == self_name
  1293. ):
  1294. # bytecode loaded the class instance to the top of stack, next load instruction
  1295. # is referencing an attribute on self
  1296. self_is_top_of_stack = True
  1297. continue
  1298. if self_is_top_of_stack and instruction.opname in (
  1299. "LOAD_ATTR",
  1300. "LOAD_METHOD",
  1301. ):
  1302. try:
  1303. ref_obj = getattr(objclass, instruction.argval)
  1304. except Exception:
  1305. ref_obj = None
  1306. if instruction.argval in invalid_names:
  1307. raise VarValueError(
  1308. f"Cached var {self._var_full_name} cannot access arbitrary state via `{instruction.argval}`."
  1309. )
  1310. if callable(ref_obj):
  1311. # recurse into callable attributes
  1312. d.update(
  1313. self._deps(
  1314. objclass=objclass,
  1315. obj=ref_obj,
  1316. )
  1317. )
  1318. # recurse into property fget functions
  1319. elif isinstance(ref_obj, property) and not isinstance(
  1320. ref_obj, ImmutableComputedVar
  1321. ):
  1322. d.update(
  1323. self._deps(
  1324. objclass=objclass,
  1325. obj=ref_obj.fget, # type: ignore
  1326. )
  1327. )
  1328. elif (
  1329. instruction.argval in objclass.backend_vars
  1330. or instruction.argval in objclass.vars
  1331. ):
  1332. # var access
  1333. d.add(instruction.argval)
  1334. elif instruction.opname == "LOAD_CONST" and isinstance(
  1335. instruction.argval, CodeType
  1336. ):
  1337. # recurse into nested functions / comprehensions, which can reference
  1338. # instance attributes from the outer scope
  1339. d.update(
  1340. self._deps(
  1341. objclass=objclass,
  1342. obj=instruction.argval,
  1343. self_name=self_name,
  1344. )
  1345. )
  1346. self_is_top_of_stack = False
  1347. return d
  1348. def mark_dirty(self, instance) -> None:
  1349. """Mark this ComputedVar as dirty.
  1350. Args:
  1351. instance: the state instance that needs to recompute the value.
  1352. """
  1353. with contextlib.suppress(AttributeError):
  1354. delattr(instance, self._cache_attr)
  1355. def _determine_var_type(self) -> Type:
  1356. """Get the type of the var.
  1357. Returns:
  1358. The type of the var.
  1359. """
  1360. hints = get_type_hints(self._fget)
  1361. if "return" in hints:
  1362. return hints["return"]
  1363. return Any
  1364. @property
  1365. def __class__(self) -> Type:
  1366. """Get the class of the var.
  1367. Returns:
  1368. The class of the var.
  1369. """
  1370. return ComputedVar
  1371. @property
  1372. def fget(self) -> Callable[[BaseState], RETURN_TYPE]:
  1373. """Get the getter function.
  1374. Returns:
  1375. The getter function.
  1376. """
  1377. return self._fget
  1378. if TYPE_CHECKING:
  1379. BASE_STATE = TypeVar("BASE_STATE", bound=BaseState)
  1380. @overload
  1381. def immutable_computed_var(
  1382. fget: None = None,
  1383. initial_value: Any | types.Unset = types.Unset(),
  1384. cache: bool = False,
  1385. deps: Optional[List[Union[str, Var]]] = None,
  1386. auto_deps: bool = True,
  1387. interval: Optional[Union[datetime.timedelta, int]] = None,
  1388. backend: bool | None = None,
  1389. **kwargs,
  1390. ) -> Callable[
  1391. [Callable[[BASE_STATE], RETURN_TYPE]], ImmutableComputedVar[RETURN_TYPE]
  1392. ]: ...
  1393. @overload
  1394. def immutable_computed_var(
  1395. fget: Callable[[BASE_STATE], RETURN_TYPE],
  1396. initial_value: RETURN_TYPE | types.Unset = types.Unset(),
  1397. cache: bool = False,
  1398. deps: Optional[List[Union[str, Var]]] = None,
  1399. auto_deps: bool = True,
  1400. interval: Optional[Union[datetime.timedelta, int]] = None,
  1401. backend: bool | None = None,
  1402. **kwargs,
  1403. ) -> ImmutableComputedVar[RETURN_TYPE]: ...
  1404. def immutable_computed_var(
  1405. fget: Callable[[BASE_STATE], Any] | None = None,
  1406. initial_value: Any | types.Unset = types.Unset(),
  1407. cache: bool = False,
  1408. deps: Optional[List[Union[str, Var]]] = None,
  1409. auto_deps: bool = True,
  1410. interval: Optional[Union[datetime.timedelta, int]] = None,
  1411. backend: bool | None = None,
  1412. **kwargs,
  1413. ) -> (
  1414. ImmutableComputedVar | Callable[[Callable[[BASE_STATE], Any]], ImmutableComputedVar]
  1415. ):
  1416. """A ComputedVar decorator with or without kwargs.
  1417. Args:
  1418. fget: The getter function.
  1419. initial_value: The initial value of the computed var.
  1420. cache: Whether to cache the computed value.
  1421. deps: Explicit var dependencies to track.
  1422. auto_deps: Whether var dependencies should be auto-determined.
  1423. interval: Interval at which the computed var should be updated.
  1424. backend: Whether the computed var is a backend var.
  1425. **kwargs: additional attributes to set on the instance
  1426. Returns:
  1427. A ComputedVar instance.
  1428. Raises:
  1429. ValueError: If caching is disabled and an update interval is set.
  1430. VarDependencyError: If user supplies dependencies without caching.
  1431. """
  1432. if cache is False and interval is not None:
  1433. raise ValueError("Cannot set update interval without caching.")
  1434. if cache is False and (deps is not None or auto_deps is False):
  1435. raise VarDependencyError("Cannot track dependencies without caching.")
  1436. if fget is not None:
  1437. return ImmutableComputedVar(fget, cache=cache)
  1438. def wrapper(fget: Callable[[BASE_STATE], Any]) -> ImmutableComputedVar:
  1439. return ImmutableComputedVar(
  1440. fget,
  1441. initial_value=initial_value,
  1442. cache=cache,
  1443. deps=deps,
  1444. auto_deps=auto_deps,
  1445. interval=interval,
  1446. backend=backend,
  1447. **kwargs,
  1448. )
  1449. return wrapper
  1450. RETURN = TypeVar("RETURN")
  1451. class CustomVarOperationReturn(ImmutableVar[RETURN]):
  1452. """Base class for custom var operations."""
  1453. @classmethod
  1454. def create(
  1455. cls,
  1456. js_expression: str,
  1457. _var_type: Type[RETURN] | None = None,
  1458. _var_data: VarData | None = None,
  1459. ) -> CustomVarOperationReturn[RETURN]:
  1460. """Create a CustomVarOperation.
  1461. Args:
  1462. js_expression: The JavaScript expression to evaluate.
  1463. _var_type: The type of the var.
  1464. _var_data: Additional hooks and imports associated with the Var.
  1465. Returns:
  1466. The CustomVarOperation.
  1467. """
  1468. return CustomVarOperationReturn(
  1469. _var_name=js_expression,
  1470. _var_type=_var_type or Any,
  1471. _var_data=ImmutableVarData.merge(_var_data),
  1472. )
  1473. def var_operation_return(
  1474. js_expression: str,
  1475. var_type: Type[RETURN] | None = None,
  1476. ) -> CustomVarOperationReturn[RETURN]:
  1477. """Shortcut for creating a CustomVarOperationReturn.
  1478. Args:
  1479. js_expression: The JavaScript expression to evaluate.
  1480. var_type: The type of the var.
  1481. Returns:
  1482. The CustomVarOperationReturn.
  1483. """
  1484. return CustomVarOperationReturn.create(js_expression, var_type)
  1485. @dataclasses.dataclass(
  1486. eq=False,
  1487. frozen=True,
  1488. **{"slots": True} if sys.version_info >= (3, 10) else {},
  1489. )
  1490. class CustomVarOperation(CachedVarOperation, ImmutableVar[T]):
  1491. """Base class for custom var operations."""
  1492. _args: Tuple[Tuple[str, Var], ...] = dataclasses.field(default_factory=tuple)
  1493. _return: CustomVarOperationReturn[T] = dataclasses.field(
  1494. default_factory=lambda: CustomVarOperationReturn.create("")
  1495. )
  1496. @cached_property_no_lock
  1497. def _cached_var_name(self) -> str:
  1498. """Get the cached var name.
  1499. Returns:
  1500. The cached var name.
  1501. """
  1502. return str(self._return)
  1503. @cached_property_no_lock
  1504. def _cached_get_all_var_data(self) -> ImmutableVarData | None:
  1505. """Get the cached VarData.
  1506. Returns:
  1507. The cached VarData.
  1508. """
  1509. return ImmutableVarData.merge(
  1510. *map(
  1511. lambda arg: arg[1]._get_all_var_data(),
  1512. self._args,
  1513. ),
  1514. self._return._get_all_var_data(),
  1515. self._var_data,
  1516. )
  1517. @classmethod
  1518. def create(
  1519. cls,
  1520. args: Tuple[Tuple[str, Var], ...],
  1521. return_var: CustomVarOperationReturn[T],
  1522. _var_data: VarData | None = None,
  1523. ) -> CustomVarOperation[T]:
  1524. """Create a CustomVarOperation.
  1525. Args:
  1526. args: The arguments to the operation.
  1527. return_var: The return var.
  1528. _var_data: Additional hooks and imports associated with the Var.
  1529. Returns:
  1530. The CustomVarOperation.
  1531. """
  1532. return CustomVarOperation(
  1533. _var_name="",
  1534. _var_type=return_var._var_type,
  1535. _var_data=ImmutableVarData.merge(_var_data),
  1536. _args=args,
  1537. _return=return_var,
  1538. )