vars.py 83 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604
  1. """Define a state var."""
  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 random
  11. import re
  12. import string
  13. import sys
  14. from types import CodeType, FunctionType
  15. from typing import (
  16. TYPE_CHECKING,
  17. Any,
  18. Callable,
  19. Dict,
  20. Iterable,
  21. List,
  22. Literal,
  23. Optional,
  24. Tuple,
  25. Type,
  26. Union,
  27. _GenericAlias, # type: ignore
  28. cast,
  29. get_args,
  30. get_origin,
  31. get_type_hints,
  32. )
  33. from reflex import constants
  34. from reflex.base import Base
  35. from reflex.utils import console, imports, serializers, types
  36. from reflex.utils.exceptions import (
  37. VarAttributeError,
  38. VarDependencyError,
  39. VarTypeError,
  40. VarValueError,
  41. )
  42. # This module used to export ImportVar itself, so we still import it for export here
  43. from reflex.utils.imports import (
  44. ImmutableParsedImportDict,
  45. ImportDict,
  46. ImportVar,
  47. ParsedImportDict,
  48. parse_imports,
  49. )
  50. from reflex.utils.types import override
  51. if TYPE_CHECKING:
  52. from reflex.state import BaseState
  53. # Set of unique variable names.
  54. USED_VARIABLES = set()
  55. # Supported operators for all types.
  56. ALL_OPS = ["==", "!=", "!==", "===", "&&", "||"]
  57. # Delimiters used between function args or operands.
  58. DELIMITERS = [","]
  59. # Mapping of valid operations for different type combinations.
  60. OPERATION_MAPPING = {
  61. (int, int): {
  62. "+",
  63. "-",
  64. "/",
  65. "//",
  66. "*",
  67. "%",
  68. "**",
  69. ">",
  70. "<",
  71. "<=",
  72. ">=",
  73. "|",
  74. "&",
  75. },
  76. (int, str): {"*"},
  77. (int, list): {"*"},
  78. (str, str): {"+", ">", "<", "<=", ">="},
  79. (float, float): {"+", "-", "/", "//", "*", "%", "**", ">", "<", "<=", ">="},
  80. (float, int): {"+", "-", "/", "//", "*", "%", "**", ">", "<", "<=", ">="},
  81. (list, list): {"+", ">", "<", "<=", ">="},
  82. }
  83. # These names were changed in reflex 0.3.0
  84. REPLACED_NAMES = {
  85. "full_name": "_var_full_name",
  86. "name": "_var_name",
  87. "state": "_var_data.state",
  88. "type_": "_var_type",
  89. "is_local": "_var_is_local",
  90. "is_string": "_var_is_string",
  91. "set_state": "_var_set_state",
  92. "deps": "_deps",
  93. }
  94. PYTHON_JS_TYPE_MAP = {
  95. (int, float): "number",
  96. (str,): "string",
  97. (bool,): "boolean",
  98. (list, tuple): "Array",
  99. (dict,): "Object",
  100. (None,): "null",
  101. }
  102. def get_unique_variable_name() -> str:
  103. """Get a unique variable name.
  104. Returns:
  105. The unique variable name.
  106. """
  107. name = "".join([random.choice(string.ascii_lowercase) for _ in range(8)])
  108. if name not in USED_VARIABLES:
  109. USED_VARIABLES.add(name)
  110. return name
  111. return get_unique_variable_name()
  112. class VarData(Base):
  113. """Metadata associated with a Var."""
  114. # The name of the enclosing state.
  115. state: str = ""
  116. # Imports needed to render this var
  117. imports: ParsedImportDict = {}
  118. # Hooks that need to be present in the component to render this var
  119. hooks: Dict[str, None] = {}
  120. # Positions of interpolated strings. This is used by the decoder to figure
  121. # out where the interpolations are and only escape the non-interpolated
  122. # segments.
  123. interpolations: List[Tuple[int, int]] = []
  124. def __init__(
  125. self, imports: Union[ImportDict, ParsedImportDict] | None = None, **kwargs: Any
  126. ):
  127. """Initialize the var data.
  128. Args:
  129. imports: The imports needed to render this var.
  130. **kwargs: The var data fields.
  131. """
  132. if imports:
  133. kwargs["imports"] = parse_imports(imports)
  134. super().__init__(**kwargs)
  135. @classmethod
  136. def merge(cls, *others: ImmutableVarData | VarData | None) -> VarData | None:
  137. """Merge multiple var data objects.
  138. Args:
  139. *others: The var data objects to merge.
  140. Returns:
  141. The merged var data object.
  142. """
  143. state = ""
  144. _imports = {}
  145. hooks = {}
  146. interpolations = []
  147. for var_data in others:
  148. if var_data is None:
  149. continue
  150. state = state or var_data.state
  151. _imports = imports.merge_imports(_imports, var_data.imports)
  152. hooks.update(
  153. var_data.hooks
  154. if isinstance(var_data.hooks, dict)
  155. else {k: None for k in var_data.hooks}
  156. )
  157. interpolations += (
  158. var_data.interpolations if isinstance(var_data, VarData) else []
  159. )
  160. return (
  161. cls(
  162. state=state,
  163. imports=_imports,
  164. hooks=hooks,
  165. interpolations=interpolations,
  166. )
  167. or None
  168. )
  169. def __bool__(self) -> bool:
  170. """Check if the var data is non-empty.
  171. Returns:
  172. True if any field is set to a non-default value.
  173. """
  174. return bool(self.state or self.imports or self.hooks or self.interpolations)
  175. def __eq__(self, other: Any) -> bool:
  176. """Check if two var data objects are equal.
  177. Args:
  178. other: The other var data object to compare.
  179. Returns:
  180. True if all fields are equal and collapsed imports are equal.
  181. """
  182. if not isinstance(other, VarData):
  183. return False
  184. # Don't compare interpolations - that's added in by the decoder, and
  185. # not part of the vardata itself.
  186. return (
  187. self.state == other.state
  188. and self.hooks.keys() == other.hooks.keys()
  189. and imports.collapse_imports(self.imports)
  190. == imports.collapse_imports(other.imports)
  191. )
  192. def dict(self) -> dict:
  193. """Convert the var data to a dictionary.
  194. Returns:
  195. The var data dictionary.
  196. """
  197. return {
  198. "state": self.state,
  199. "interpolations": list(self.interpolations),
  200. "imports": {
  201. lib: [import_var.dict() for import_var in import_vars]
  202. for lib, import_vars in self.imports.items()
  203. },
  204. "hooks": self.hooks,
  205. }
  206. @dataclasses.dataclass(
  207. eq=True,
  208. frozen=True,
  209. )
  210. class ImmutableVarData:
  211. """Metadata associated with a Var."""
  212. # The name of the enclosing state.
  213. state: str = dataclasses.field(default="")
  214. # Imports needed to render this var
  215. imports: ImmutableParsedImportDict = dataclasses.field(default_factory=tuple)
  216. # Hooks that need to be present in the component to render this var
  217. hooks: Tuple[str, ...] = dataclasses.field(default_factory=tuple)
  218. def __init__(
  219. self,
  220. state: str = "",
  221. imports: ImportDict | ParsedImportDict | None = None,
  222. hooks: dict[str, None] | None = None,
  223. ):
  224. """Initialize the var data.
  225. Args:
  226. state: The name of the enclosing state.
  227. imports: Imports needed to render this var.
  228. hooks: Hooks that need to be present in the component to render this var.
  229. """
  230. immutable_imports: ImmutableParsedImportDict = tuple(
  231. sorted(
  232. ((k, tuple(sorted(v))) for k, v in parse_imports(imports or {}).items())
  233. )
  234. )
  235. object.__setattr__(self, "state", state)
  236. object.__setattr__(self, "imports", immutable_imports)
  237. object.__setattr__(self, "hooks", tuple(hooks or {}))
  238. @classmethod
  239. def merge(
  240. cls, *others: ImmutableVarData | VarData | None
  241. ) -> ImmutableVarData | None:
  242. """Merge multiple var data objects.
  243. Args:
  244. *others: The var data objects to merge.
  245. Returns:
  246. The merged var data object.
  247. """
  248. state = ""
  249. _imports = {}
  250. hooks = {}
  251. for var_data in others:
  252. if var_data is None:
  253. continue
  254. state = state or var_data.state
  255. _imports = imports.merge_imports(_imports, var_data.imports)
  256. hooks.update(
  257. var_data.hooks
  258. if isinstance(var_data.hooks, dict)
  259. else {k: None for k in var_data.hooks}
  260. )
  261. return (
  262. ImmutableVarData(
  263. state=state,
  264. imports=_imports,
  265. hooks=hooks,
  266. )
  267. or None
  268. )
  269. def __bool__(self) -> bool:
  270. """Check if the var data is non-empty.
  271. Returns:
  272. True if any field is set to a non-default value.
  273. """
  274. return bool(self.state or self.imports or self.hooks)
  275. def __eq__(self, other: Any) -> bool:
  276. """Check if two var data objects are equal.
  277. Args:
  278. other: The other var data object to compare.
  279. Returns:
  280. True if all fields are equal and collapsed imports are equal.
  281. """
  282. if not isinstance(other, (ImmutableVarData, VarData)):
  283. return False
  284. # Don't compare interpolations - that's added in by the decoder, and
  285. # not part of the vardata itself.
  286. return (
  287. self.state == other.state
  288. and self.hooks
  289. == (
  290. other.hooks
  291. if isinstance(other, ImmutableVarData)
  292. else tuple(other.hooks.keys())
  293. )
  294. and imports.collapse_imports(self.imports)
  295. == imports.collapse_imports(other.imports)
  296. )
  297. def _decode_var_immutable(value: str) -> tuple[ImmutableVarData | None, str]:
  298. """Decode the state name from a formatted var.
  299. Args:
  300. value: The value to extract the state name from.
  301. Returns:
  302. The extracted state name and the value without the state name.
  303. """
  304. var_datas = []
  305. if isinstance(value, str):
  306. # fast path if there is no encoded VarData
  307. if constants.REFLEX_VAR_OPENING_TAG not in value:
  308. return None, value
  309. offset = 0
  310. # Initialize some methods for reading json.
  311. var_data_config = VarData().__config__
  312. def json_loads(s):
  313. try:
  314. return var_data_config.json_loads(s)
  315. except json.decoder.JSONDecodeError:
  316. return var_data_config.json_loads(var_data_config.json_loads(f'"{s}"'))
  317. # Find all tags.
  318. while m := _decode_var_pattern.search(value):
  319. start, end = m.span()
  320. value = value[:start] + value[end:]
  321. serialized_data = m.group(1)
  322. if serialized_data.isnumeric() or (
  323. serialized_data[0] == "-" and serialized_data[1:].isnumeric()
  324. ):
  325. # This is a global immutable var.
  326. var = _global_vars[int(serialized_data)]
  327. var_data = var._var_data
  328. if var_data is not None:
  329. realstart = start + offset
  330. var_datas.append(var_data)
  331. else:
  332. # Read the JSON, pull out the string length, parse the rest as VarData.
  333. data = json_loads(serialized_data)
  334. string_length = data.pop("string_length", None)
  335. var_data = VarData.parse_obj(data)
  336. # Use string length to compute positions of interpolations.
  337. if string_length is not None:
  338. realstart = start + offset
  339. var_data.interpolations = [(realstart, realstart + string_length)]
  340. var_datas.append(var_data)
  341. offset += end - start
  342. return ImmutableVarData.merge(*var_datas) if var_datas else None, value
  343. def _encode_var(value: Var) -> str:
  344. """Encode the state name into a formatted var.
  345. Args:
  346. value: The value to encode the state name into.
  347. Returns:
  348. The encoded var.
  349. """
  350. if value._var_data:
  351. from reflex.utils.serializers import serialize
  352. final_value = str(value)
  353. data = value._var_data.dict()
  354. data["string_length"] = len(final_value)
  355. data_json = value._var_data.__config__.json_dumps(data, default=serialize)
  356. return (
  357. f"{constants.REFLEX_VAR_OPENING_TAG}{data_json}{constants.REFLEX_VAR_CLOSING_TAG}"
  358. + final_value
  359. )
  360. return str(value)
  361. # Compile regex for finding reflex var tags.
  362. _decode_var_pattern_re = (
  363. rf"{constants.REFLEX_VAR_OPENING_TAG}(.*?){constants.REFLEX_VAR_CLOSING_TAG}"
  364. )
  365. _decode_var_pattern = re.compile(_decode_var_pattern_re, flags=re.DOTALL)
  366. # Defined global immutable vars.
  367. _global_vars: Dict[int, Var] = {}
  368. def _decode_var(value: str) -> tuple[VarData | None, str]:
  369. """Decode the state name from a formatted var.
  370. Args:
  371. value: The value to extract the state name from.
  372. Returns:
  373. The extracted state name and the value without the state name.
  374. """
  375. var_datas = []
  376. if isinstance(value, str):
  377. # fast path if there is no encoded VarData
  378. if constants.REFLEX_VAR_OPENING_TAG not in value:
  379. return None, value
  380. offset = 0
  381. # Initialize some methods for reading json.
  382. var_data_config = VarData().__config__
  383. def json_loads(s):
  384. try:
  385. return var_data_config.json_loads(s)
  386. except json.decoder.JSONDecodeError:
  387. return var_data_config.json_loads(var_data_config.json_loads(f'"{s}"'))
  388. # Find all tags.
  389. while m := _decode_var_pattern.search(value):
  390. start, end = m.span()
  391. value = value[:start] + value[end:]
  392. serialized_data = m.group(1)
  393. if serialized_data.isnumeric() or (
  394. serialized_data[0] == "-" and serialized_data[1:].isnumeric()
  395. ):
  396. # This is a global immutable var.
  397. var = _global_vars[int(serialized_data)]
  398. var_data = var._var_data
  399. if var_data is not None:
  400. realstart = start + offset
  401. var_datas.append(var_data)
  402. else:
  403. # Read the JSON, pull out the string length, parse the rest as VarData.
  404. data = json_loads(serialized_data)
  405. string_length = data.pop("string_length", None)
  406. var_data = VarData.parse_obj(data)
  407. # Use string length to compute positions of interpolations.
  408. if string_length is not None:
  409. realstart = start + offset
  410. var_data.interpolations = [(realstart, realstart + string_length)]
  411. var_datas.append(var_data)
  412. offset += end - start
  413. return VarData.merge(*var_datas) if var_datas else None, value
  414. def _extract_var_data(value: Iterable) -> list[VarData | None]:
  415. """Extract the var imports and hooks from an iterable containing a Var.
  416. Args:
  417. value: The iterable to extract the VarData from
  418. Returns:
  419. The extracted VarDatas.
  420. """
  421. from reflex.style import Style
  422. var_datas = []
  423. with contextlib.suppress(TypeError):
  424. for sub in value:
  425. if isinstance(sub, Var):
  426. var_datas.append(sub._var_data)
  427. elif not isinstance(sub, str):
  428. # Recurse into dict values.
  429. if hasattr(sub, "values") and callable(sub.values):
  430. var_datas.extend(_extract_var_data(sub.values()))
  431. # Recurse into iterable values (or dict keys).
  432. var_datas.extend(_extract_var_data(sub))
  433. # Style objects should already have _var_data.
  434. if isinstance(value, Style):
  435. var_datas.append(value._var_data)
  436. else:
  437. # Recurse when value is a dict itself.
  438. values = getattr(value, "values", None)
  439. if callable(values):
  440. var_datas.extend(_extract_var_data(values()))
  441. return var_datas
  442. class Var:
  443. """An abstract var."""
  444. # The name of the var.
  445. _var_name: str
  446. # The type of the var.
  447. _var_type: Type
  448. # Whether this is a local javascript variable.
  449. _var_is_local: bool
  450. # Whether the var is a string literal.
  451. _var_is_string: bool
  452. # _var_full_name should be prefixed with _var_state
  453. _var_full_name_needs_state_prefix: bool
  454. # Extra metadata associated with the Var
  455. _var_data: Optional[VarData]
  456. @classmethod
  457. def create(
  458. cls,
  459. value: Any,
  460. _var_is_local: bool = True,
  461. _var_is_string: bool | None = None,
  462. _var_data: Optional[VarData] = None,
  463. ) -> Var | None:
  464. """Create a var from a value.
  465. Args:
  466. value: The value to create the var from.
  467. _var_is_local: Whether the var is local.
  468. _var_is_string: Whether the var is a string literal.
  469. _var_data: Additional hooks and imports associated with the Var.
  470. Returns:
  471. The var.
  472. Raises:
  473. VarTypeError: If the value is JSON-unserializable.
  474. """
  475. from reflex.utils import format
  476. # Check for none values.
  477. if value is None:
  478. return None
  479. # If the value is already a var, do nothing.
  480. if isinstance(value, Var):
  481. return value
  482. # Try to pull the imports and hooks from contained values.
  483. if not isinstance(value, str):
  484. _var_data = VarData.merge(*_extract_var_data(value), _var_data)
  485. # Try to serialize the value.
  486. type_ = type(value)
  487. if type_ in types.JSONType:
  488. name = value
  489. else:
  490. name, serialized_type = serializers.serialize(value, get_type=True)
  491. if (
  492. serialized_type is not None
  493. and _var_is_string is None
  494. and issubclass(serialized_type, str)
  495. ):
  496. _var_is_string = True
  497. if name is None:
  498. raise VarTypeError(
  499. f"No JSON serializer found for var {value} of type {type_}."
  500. )
  501. name = name if isinstance(name, str) else format.json_dumps(name)
  502. if _var_is_string is None and type_ is str:
  503. console.deprecate(
  504. feature_name=f"Creating a Var ({value}) from a string without specifying _var_is_string",
  505. reason=(
  506. "Specify _var_is_string=False to create a Var that is not a string literal. "
  507. "In the future, creating a Var from a string will be treated as a string literal "
  508. "by default."
  509. ),
  510. deprecation_version="0.5.4",
  511. removal_version="0.6.0",
  512. )
  513. return BaseVar(
  514. _var_name=name,
  515. _var_type=type_,
  516. _var_is_local=_var_is_local,
  517. _var_is_string=_var_is_string if _var_is_string is not None else False,
  518. _var_data=_var_data,
  519. )
  520. @classmethod
  521. def create_safe(
  522. cls,
  523. value: Any,
  524. _var_is_local: bool = True,
  525. _var_is_string: bool | None = None,
  526. _var_data: Optional[VarData] = None,
  527. ) -> Var:
  528. """Create a var from a value, asserting that it is not None.
  529. Args:
  530. value: The value to create the var from.
  531. _var_is_local: Whether the var is local.
  532. _var_is_string: Whether the var is a string literal.
  533. _var_data: Additional hooks and imports associated with the Var.
  534. Returns:
  535. The var.
  536. """
  537. var = cls.create(
  538. value,
  539. _var_is_local=_var_is_local,
  540. _var_is_string=_var_is_string,
  541. _var_data=_var_data,
  542. )
  543. assert var is not None
  544. return var
  545. @classmethod
  546. def __class_getitem__(cls, type_: str) -> _GenericAlias:
  547. """Get a typed var.
  548. Args:
  549. type_: The type of the var.
  550. Returns:
  551. The var class item.
  552. """
  553. return _GenericAlias(cls, type_)
  554. def __post_init__(self) -> None:
  555. """Post-initialize the var."""
  556. # Decode any inline Var markup and apply it to the instance
  557. _var_data, _var_name = _decode_var(self._var_name)
  558. if _var_data:
  559. self._var_name = _var_name
  560. self._var_data = VarData.merge(self._var_data, _var_data)
  561. def _replace(self, merge_var_data=None, **kwargs: Any) -> BaseVar:
  562. """Make a copy of this Var with updated fields.
  563. Args:
  564. merge_var_data: VarData to merge into the existing VarData.
  565. **kwargs: Var fields to update.
  566. Returns:
  567. A new BaseVar with the updated fields overwriting the corresponding fields in this Var.
  568. Raises:
  569. TypeError: If kwargs contains keys that are not allowed.
  570. """
  571. field_values = dict(
  572. _var_name=kwargs.pop("_var_name", self._var_name),
  573. _var_type=kwargs.pop("_var_type", self._var_type),
  574. _var_is_local=kwargs.pop("_var_is_local", self._var_is_local),
  575. _var_is_string=kwargs.pop("_var_is_string", self._var_is_string),
  576. _var_full_name_needs_state_prefix=kwargs.pop(
  577. "_var_full_name_needs_state_prefix",
  578. self._var_full_name_needs_state_prefix,
  579. ),
  580. _var_data=VarData.merge(
  581. kwargs.pop("_var_data", self._var_data), merge_var_data
  582. ),
  583. )
  584. if kwargs:
  585. unexpected_kwargs = ", ".join(kwargs.keys())
  586. raise TypeError(f"Unexpected keyword arguments: {unexpected_kwargs}")
  587. return BaseVar(**field_values)
  588. def _decode(self) -> Any:
  589. """Decode Var as a python value.
  590. Note that Var with state set cannot be decoded python-side and will be
  591. returned as full_name.
  592. Returns:
  593. The decoded value or the Var name.
  594. """
  595. if self._var_is_string:
  596. return self._var_name
  597. try:
  598. return json.loads(self._var_name)
  599. except ValueError:
  600. return self._var_name
  601. def equals(self, other: Var) -> bool:
  602. """Check if two vars are equal.
  603. Args:
  604. other: The other var to compare.
  605. Returns:
  606. Whether the vars are equal.
  607. """
  608. return (
  609. self._var_name == other._var_name
  610. and self._var_type == other._var_type
  611. and self._var_is_local == other._var_is_local
  612. and self._var_full_name_needs_state_prefix
  613. == other._var_full_name_needs_state_prefix
  614. and self._var_data == other._var_data
  615. )
  616. def _merge(self, other) -> Var:
  617. """Merge two or more dicts.
  618. Args:
  619. other: The other var to merge.
  620. Returns:
  621. The merged var.
  622. """
  623. if other is None:
  624. return self._replace()
  625. if not isinstance(other, Var):
  626. other = Var.create(other, _var_is_string=False)
  627. return self._replace(
  628. _var_name=f"{{...{self._var_name}, ...{other._var_name}}}" # type: ignore
  629. )
  630. def to_string(self, json: bool = True) -> Var:
  631. """Convert a var to a string.
  632. Args:
  633. json: Whether to convert to a JSON string.
  634. Returns:
  635. The stringified var.
  636. """
  637. fn = "JSON.stringify" if json else "String"
  638. return self.operation(fn=fn, type_=str)
  639. def to_int(self) -> Var:
  640. """Convert a var to an int.
  641. Returns:
  642. The parseInt var.
  643. """
  644. return self.operation(fn="parseInt", type_=int)
  645. def __hash__(self) -> int:
  646. """Define a hash function for a var.
  647. Returns:
  648. The hash of the var.
  649. """
  650. return hash((self._var_name, str(self._var_type)))
  651. def __str__(self) -> str:
  652. """Wrap the var so it can be used in templates.
  653. Returns:
  654. The wrapped var, i.e. {state.var}.
  655. """
  656. from reflex.utils import format
  657. out = (
  658. self._var_full_name
  659. if self._var_is_local
  660. else format.wrap(self._var_full_name, "{")
  661. )
  662. if self._var_is_string:
  663. out = format.format_string(out)
  664. return out
  665. def __bool__(self) -> bool:
  666. """Raise exception if using Var in a boolean context.
  667. Raises:
  668. VarTypeError: when attempting to bool-ify the Var.
  669. """
  670. raise VarTypeError(
  671. f"Cannot convert Var {self._var_full_name!r} to bool for use with `if`, `and`, `or`, and `not`. "
  672. "Instead use `rx.cond` and bitwise operators `&` (and), `|` (or), `~` (invert)."
  673. )
  674. def __iter__(self) -> Any:
  675. """Raise exception if using Var in an iterable context.
  676. Raises:
  677. VarTypeError: when attempting to iterate over the Var.
  678. """
  679. raise VarTypeError(
  680. f"Cannot iterate over Var {self._var_full_name!r}. Instead use `rx.foreach`."
  681. )
  682. def __format__(self, format_spec: str) -> str:
  683. """Format the var into a Javascript equivalent to an f-string.
  684. Args:
  685. format_spec: The format specifier (Ignored for now).
  686. Returns:
  687. The formatted var.
  688. """
  689. # Encode the _var_data into the formatted output for tracking purposes.
  690. str_self = _encode_var(self)
  691. if self._var_is_local:
  692. return str_self
  693. return f"${str_self}"
  694. def __getitem__(self, i: Any) -> Var:
  695. """Index into a var.
  696. Args:
  697. i: The index to index into.
  698. Returns:
  699. The indexed var.
  700. Raises:
  701. VarTypeError: If the var is not indexable.
  702. """
  703. from reflex.utils import format
  704. # Indexing is only supported for strings, lists, tuples, dicts, and dataframes.
  705. if not (
  706. types._issubclass(self._var_type, Union[List, Dict, Tuple, str])
  707. or types.is_dataframe(self._var_type)
  708. ):
  709. if self._var_type == Any:
  710. raise VarTypeError(
  711. "Could not index into var of type Any. (If you are trying to index into a state var, "
  712. "add the correct type annotation to the var.)"
  713. )
  714. raise VarTypeError(
  715. f"Var {self._var_name} of type {self._var_type} does not support indexing."
  716. )
  717. # The type of the indexed var.
  718. type_ = Any
  719. # Convert any vars to local vars.
  720. if isinstance(i, Var):
  721. i = i._replace(_var_is_local=True)
  722. # Handle list/tuple/str indexing.
  723. if types._issubclass(self._var_type, Union[List, Tuple, str]):
  724. # List/Tuple/String indices must be ints, slices, or vars.
  725. if (
  726. not isinstance(i, types.get_args(Union[int, slice, Var]))
  727. or isinstance(i, Var)
  728. and not i._var_type == int
  729. ):
  730. raise VarTypeError("Index must be an integer or an integer var.")
  731. # Handle slices first.
  732. if isinstance(i, slice):
  733. # Get the start and stop indices.
  734. start = i.start or 0
  735. stop = i.stop or "undefined"
  736. # Use the slice function.
  737. return self._replace(
  738. _var_name=f"{self._var_name}.slice({start}, {stop})",
  739. _var_is_string=False,
  740. )
  741. # Get the type of the indexed var.
  742. if types.is_generic_alias(self._var_type):
  743. index = i if not isinstance(i, Var) else 0
  744. type_ = types.get_args(self._var_type)
  745. type_ = type_[index % len(type_)] if type_ else Any
  746. elif types._issubclass(self._var_type, str):
  747. type_ = str
  748. # Use `at` to support negative indices.
  749. return self._replace(
  750. _var_name=f"{self._var_name}.at({i})",
  751. _var_type=type_,
  752. _var_is_string=False,
  753. )
  754. # Dictionary / dataframe indexing.
  755. # Tuples are currently not supported as indexes.
  756. if (
  757. (
  758. types._issubclass(self._var_type, Dict)
  759. or types.is_dataframe(self._var_type)
  760. )
  761. and not isinstance(i, types.get_args(Union[int, str, float, Var]))
  762. ) or (
  763. isinstance(i, Var)
  764. and not types._issubclass(
  765. i._var_type, types.get_args(Union[int, str, float])
  766. )
  767. ):
  768. raise VarTypeError(
  769. "Index must be one of the following types: int, str, int or str Var"
  770. )
  771. # Get the type of the indexed var.
  772. if isinstance(i, str):
  773. i = format.wrap(i, '"')
  774. type_ = (
  775. types.get_args(self._var_type)[1]
  776. if types.is_generic_alias(self._var_type)
  777. else Any
  778. )
  779. # Use normal indexing here.
  780. return self._replace(
  781. _var_name=f"{self._var_name}[{i}]",
  782. _var_type=type_,
  783. _var_is_string=False,
  784. )
  785. def __getattribute__(self, name: str) -> Any:
  786. """Get a var attribute.
  787. Args:
  788. name: The name of the attribute.
  789. Returns:
  790. The var attribute.
  791. Raises:
  792. VarAttributeError: If the attribute cannot be found, or if __getattr__ fallback should be used.
  793. """
  794. try:
  795. var_attribute = super().__getattribute__(name)
  796. if (
  797. not name.startswith("_")
  798. and name not in Var.__dict__
  799. and name not in BaseVar.__dict__
  800. ):
  801. # Check if the attribute should be accessed through the Var instead of
  802. # accessing one of the Var operations
  803. type_ = types.get_attribute_access_type(
  804. super().__getattribute__("_var_type"), name
  805. )
  806. if type_ is not None:
  807. raise VarAttributeError(
  808. f"{name} is being accessed through the Var."
  809. )
  810. # Return the attribute as-is.
  811. return var_attribute
  812. except VarAttributeError:
  813. raise # fall back to __getattr__ anyway
  814. def __getattr__(self, name: str) -> Var:
  815. """Get a var attribute.
  816. Args:
  817. name: The name of the attribute.
  818. Returns:
  819. The var attribute.
  820. Raises:
  821. VarAttributeError: If the var is wrongly annotated or can't find attribute.
  822. VarTypeError: If an annotation to the var isn't provided.
  823. """
  824. # Check if the attribute is one of the class fields.
  825. if not name.startswith("_"):
  826. if self._var_type == Any:
  827. raise VarTypeError(
  828. f"You must provide an annotation for the state var `{self._var_full_name}`. Annotation cannot be `{self._var_type}`"
  829. ) from None
  830. is_optional = types.is_optional(self._var_type)
  831. type_ = types.get_attribute_access_type(self._var_type, name)
  832. if type_ is not None:
  833. return self._replace(
  834. _var_name=f"{self._var_name}{'?' if is_optional else ''}.{name}",
  835. _var_type=type_,
  836. _var_is_string=False,
  837. )
  838. if name in REPLACED_NAMES:
  839. raise VarAttributeError(
  840. f"Field {name!r} was renamed to {REPLACED_NAMES[name]!r}"
  841. )
  842. raise VarAttributeError(
  843. f"The State var `{self._var_full_name}` has no attribute '{name}' or may have been annotated "
  844. f"wrongly."
  845. )
  846. raise VarAttributeError(
  847. f"The State var has no attribute '{name}' or may have been annotated wrongly.",
  848. )
  849. def operation(
  850. self,
  851. op: str = "",
  852. other: Var | None = None,
  853. type_: Type | None = None,
  854. flip: bool = False,
  855. fn: str | None = None,
  856. invoke_fn: bool = False,
  857. ) -> Var:
  858. """Perform an operation on a var.
  859. Args:
  860. op: The operation to perform.
  861. other: The other var to perform the operation on.
  862. type_: The type of the operation result.
  863. flip: Whether to flip the order of the operation.
  864. fn: A function to apply to the operation.
  865. invoke_fn: Whether to invoke the function.
  866. Returns:
  867. The operation result.
  868. Raises:
  869. VarTypeError: If the operation between two operands is invalid.
  870. VarValueError: If flip is set to true and value of operand is not provided
  871. """
  872. from reflex.utils import format
  873. if isinstance(other, str):
  874. other = Var.create(json.dumps(other), _var_is_string=False)
  875. else:
  876. other = Var.create(other, _var_is_string=False)
  877. type_ = type_ or self._var_type
  878. if other is None and flip:
  879. raise VarValueError(
  880. "flip_operands cannot be set to True if the value of 'other' operand is not provided"
  881. )
  882. left_operand, right_operand = (other, self) if flip else (self, other)
  883. def get_operand_full_name(operand):
  884. # operand vars that are string literals need to be wrapped in back ticks.
  885. return (
  886. operand._var_name_unwrapped
  887. if operand._var_is_string
  888. and not operand._var_state
  889. and operand._var_is_local
  890. else operand._var_full_name
  891. )
  892. if other is not None:
  893. # check if the operation between operands is valid.
  894. if op and not self.is_valid_operation(
  895. types.get_base_class(left_operand._var_type), # type: ignore
  896. types.get_base_class(right_operand._var_type), # type: ignore
  897. op,
  898. ):
  899. raise VarTypeError(
  900. f"Unsupported Operand type(s) for {op}: `{left_operand._var_full_name}` of type {left_operand._var_type.__name__} and `{right_operand._var_full_name}` of type {right_operand._var_type.__name__}" # type: ignore
  901. )
  902. left_operand_full_name = get_operand_full_name(left_operand)
  903. right_operand_full_name = get_operand_full_name(right_operand)
  904. left_operand_full_name = format.wrap(left_operand_full_name, "(")
  905. right_operand_full_name = format.wrap(right_operand_full_name, "(")
  906. # apply function to operands
  907. if fn is not None:
  908. if invoke_fn:
  909. # invoke the function on left operand.
  910. operation_name = (
  911. f"{left_operand_full_name}.{fn}({right_operand_full_name})" # type: ignore
  912. )
  913. else:
  914. # pass the operands as arguments to the function.
  915. operation_name = (
  916. f"{left_operand_full_name} {op} {right_operand_full_name}" # type: ignore
  917. )
  918. operation_name = f"{fn}({operation_name})"
  919. else:
  920. # apply operator to operands (left operand <operator> right_operand)
  921. operation_name = (
  922. f"{left_operand_full_name} {op} {right_operand_full_name}" # type: ignore
  923. )
  924. operation_name = format.wrap(operation_name, "(")
  925. else:
  926. # apply operator to left operand (<operator> left_operand)
  927. operation_name = f"{op}{get_operand_full_name(self)}"
  928. # apply function to operands
  929. if fn is not None:
  930. operation_name = (
  931. f"{fn}({operation_name})"
  932. if not invoke_fn
  933. else f"{get_operand_full_name(self)}.{fn}()"
  934. )
  935. return self._replace(
  936. _var_name=operation_name,
  937. _var_type=type_,
  938. _var_is_string=False,
  939. _var_full_name_needs_state_prefix=False,
  940. merge_var_data=other._var_data if other is not None else None,
  941. )
  942. @staticmethod
  943. def is_valid_operation(
  944. operand1_type: Type, operand2_type: Type, operator: str
  945. ) -> bool:
  946. """Check if an operation between two operands is valid.
  947. Args:
  948. operand1_type: Type of the operand
  949. operand2_type: Type of the second operand
  950. operator: The operator.
  951. Returns:
  952. Whether operation is valid or not
  953. """
  954. if operator in ALL_OPS or operator in DELIMITERS:
  955. return True
  956. # bools are subclasses of ints
  957. pair = tuple(
  958. sorted(
  959. [
  960. int if operand1_type == bool else operand1_type,
  961. int if operand2_type == bool else operand2_type,
  962. ],
  963. key=lambda x: x.__name__,
  964. )
  965. )
  966. return pair in OPERATION_MAPPING and operator in OPERATION_MAPPING[pair]
  967. def compare(self, op: str, other: Var) -> Var:
  968. """Compare two vars with inequalities.
  969. Args:
  970. op: The comparison operator.
  971. other: The other var to compare with.
  972. Returns:
  973. The comparison result.
  974. """
  975. return self.operation(op, other, bool)
  976. def __invert__(self) -> Var:
  977. """Invert a var.
  978. Returns:
  979. The inverted var.
  980. """
  981. return self.operation("!", type_=bool)
  982. def __neg__(self) -> Var:
  983. """Negate a var.
  984. Returns:
  985. The negated var.
  986. """
  987. return self.operation(fn="-")
  988. def __abs__(self) -> Var:
  989. """Get the absolute value of a var.
  990. Returns:
  991. A var with the absolute value.
  992. """
  993. return self.operation(fn="Math.abs")
  994. def length(self) -> Var:
  995. """Get the length of a list var.
  996. Returns:
  997. A var with the absolute value.
  998. Raises:
  999. VarTypeError: If the var is not a list.
  1000. """
  1001. if not types._issubclass(self._var_type, List):
  1002. raise VarTypeError(f"Cannot get length of non-list var {self}.")
  1003. return self._replace(
  1004. _var_name=f"{self._var_name}.length",
  1005. _var_type=int,
  1006. _var_is_string=False,
  1007. )
  1008. def _type(self) -> Var:
  1009. """Get the type of the Var in Javascript.
  1010. Returns:
  1011. A var representing the type check.
  1012. """
  1013. return self._replace(
  1014. _var_name=f"typeof {self._var_full_name}",
  1015. _var_type=str,
  1016. _var_is_string=False,
  1017. _var_full_name_needs_state_prefix=False,
  1018. )
  1019. def __eq__(self, other: Union[Var, Type]) -> Var:
  1020. """Perform an equality comparison.
  1021. Args:
  1022. other: The other var to compare with.
  1023. Returns:
  1024. A var representing the equality comparison.
  1025. """
  1026. for python_types, js_type in PYTHON_JS_TYPE_MAP.items():
  1027. if not isinstance(other, Var) and other in python_types:
  1028. return self.compare("===", Var.create(js_type, _var_is_string=True)) # type: ignore
  1029. return self.compare("===", other)
  1030. def __ne__(self, other: Union[Var, Type]) -> Var:
  1031. """Perform an inequality comparison.
  1032. Args:
  1033. other: The other var to compare with.
  1034. Returns:
  1035. A var representing the inequality comparison.
  1036. """
  1037. for python_types, js_type in PYTHON_JS_TYPE_MAP.items():
  1038. if not isinstance(other, Var) and other in python_types:
  1039. return self.compare("!==", Var.create(js_type, _var_is_string=True)) # type: ignore
  1040. return self.compare("!==", other)
  1041. def __gt__(self, other: Var) -> Var:
  1042. """Perform a greater than comparison.
  1043. Args:
  1044. other: The other var to compare with.
  1045. Returns:
  1046. A var representing the greater than comparison.
  1047. """
  1048. return self.compare(">", other)
  1049. def __ge__(self, other: Var) -> Var:
  1050. """Perform a greater than or equal to comparison.
  1051. Args:
  1052. other: The other var to compare with.
  1053. Returns:
  1054. A var representing the greater than or equal to comparison.
  1055. """
  1056. return self.compare(">=", other)
  1057. def __lt__(self, other: Var) -> Var:
  1058. """Perform a less than comparison.
  1059. Args:
  1060. other: The other var to compare with.
  1061. Returns:
  1062. A var representing the less than comparison.
  1063. """
  1064. return self.compare("<", other)
  1065. def __le__(self, other: Var) -> Var:
  1066. """Perform a less than or equal to comparison.
  1067. Args:
  1068. other: The other var to compare with.
  1069. Returns:
  1070. A var representing the less than or equal to comparison.
  1071. """
  1072. return self.compare("<=", other)
  1073. def __add__(self, other: Var, flip=False) -> Var:
  1074. """Add two vars.
  1075. Args:
  1076. other: The other var to add.
  1077. flip: Whether to flip operands.
  1078. Returns:
  1079. A var representing the sum.
  1080. """
  1081. other_type = other._var_type if isinstance(other, Var) else type(other)
  1082. # For list-list addition, javascript concatenates the content of the lists instead of
  1083. # merging the list, and for that reason we use the spread operator available through spreadArraysOrObjects
  1084. # utility function
  1085. if (
  1086. types.get_base_class(self._var_type) == list
  1087. and types.get_base_class(other_type) == list
  1088. ):
  1089. return self.operation(
  1090. ",", other, fn="spreadArraysOrObjects", flip=flip
  1091. )._replace(
  1092. merge_var_data=VarData(
  1093. imports={
  1094. f"/{constants.Dirs.STATE_PATH}": [
  1095. ImportVar(tag="spreadArraysOrObjects")
  1096. ]
  1097. },
  1098. ),
  1099. )
  1100. return self.operation("+", other, flip=flip)
  1101. def __radd__(self, other: Var) -> Var:
  1102. """Add two vars.
  1103. Args:
  1104. other: The other var to add.
  1105. Returns:
  1106. A var representing the sum.
  1107. """
  1108. return self.__add__(other=other, flip=True)
  1109. def __sub__(self, other: Var) -> Var:
  1110. """Subtract two vars.
  1111. Args:
  1112. other: The other var to subtract.
  1113. Returns:
  1114. A var representing the difference.
  1115. """
  1116. return self.operation("-", other)
  1117. def __rsub__(self, other: Var) -> Var:
  1118. """Subtract two vars.
  1119. Args:
  1120. other: The other var to subtract.
  1121. Returns:
  1122. A var representing the difference.
  1123. """
  1124. return self.operation("-", other, flip=True)
  1125. def __mul__(self, other: Var, flip=True) -> Var:
  1126. """Multiply two vars.
  1127. Args:
  1128. other: The other var to multiply.
  1129. flip: Whether to flip operands
  1130. Returns:
  1131. A var representing the product.
  1132. """
  1133. other_type = other._var_type if isinstance(other, Var) else type(other)
  1134. # For str-int multiplication, we use the repeat function.
  1135. # i.e "hello" * 2 is equivalent to "hello".repeat(2) in js.
  1136. if (types.get_base_class(self._var_type), types.get_base_class(other_type)) in [
  1137. (int, str),
  1138. (str, int),
  1139. ]:
  1140. return self.operation(other=other, fn="repeat", invoke_fn=True)
  1141. # For list-int multiplication, we use the Array function.
  1142. # i.e ["hello"] * 2 is equivalent to Array(2).fill().map(() => ["hello"]).flat() in js.
  1143. if (types.get_base_class(self._var_type), types.get_base_class(other_type)) in [
  1144. (int, list),
  1145. (list, int),
  1146. ]:
  1147. other_name = other._var_full_name if isinstance(other, Var) else other
  1148. name = f"Array({other_name}).fill().map(() => {self._var_full_name}).flat()"
  1149. return self._replace(
  1150. _var_name=name,
  1151. _var_type=str,
  1152. _var_is_string=False,
  1153. _var_full_name_needs_state_prefix=False,
  1154. )
  1155. return self.operation("*", other)
  1156. def __rmul__(self, other: Var) -> Var:
  1157. """Multiply two vars.
  1158. Args:
  1159. other: The other var to multiply.
  1160. Returns:
  1161. A var representing the product.
  1162. """
  1163. return self.__mul__(other=other, flip=True)
  1164. def __pow__(self, other: Var) -> Var:
  1165. """Raise a var to a power.
  1166. Args:
  1167. other: The power to raise to.
  1168. Returns:
  1169. A var representing the power.
  1170. """
  1171. return self.operation(",", other, fn="Math.pow")
  1172. def __rpow__(self, other: Var) -> Var:
  1173. """Raise a var to a power.
  1174. Args:
  1175. other: The power to raise to.
  1176. Returns:
  1177. A var representing the power.
  1178. """
  1179. return self.operation(",", other, flip=True, fn="Math.pow")
  1180. def __truediv__(self, other: Var) -> Var:
  1181. """Divide two vars.
  1182. Args:
  1183. other: The other var to divide.
  1184. Returns:
  1185. A var representing the quotient.
  1186. """
  1187. return self.operation("/", other)
  1188. def __rtruediv__(self, other: Var) -> Var:
  1189. """Divide two vars.
  1190. Args:
  1191. other: The other var to divide.
  1192. Returns:
  1193. A var representing the quotient.
  1194. """
  1195. return self.operation("/", other, flip=True)
  1196. def __floordiv__(self, other: Var) -> Var:
  1197. """Divide two vars.
  1198. Args:
  1199. other: The other var to divide.
  1200. Returns:
  1201. A var representing the quotient.
  1202. """
  1203. return self.operation("/", other, fn="Math.floor")
  1204. def __mod__(self, other: Var) -> Var:
  1205. """Get the remainder of two vars.
  1206. Args:
  1207. other: The other var to divide.
  1208. Returns:
  1209. A var representing the remainder.
  1210. """
  1211. return self.operation("%", other)
  1212. def __rmod__(self, other: Var) -> Var:
  1213. """Get the remainder of two vars.
  1214. Args:
  1215. other: The other var to divide.
  1216. Returns:
  1217. A var representing the remainder.
  1218. """
  1219. return self.operation("%", other, flip=True)
  1220. def __and__(self, other: Var) -> Var:
  1221. """Perform a logical and.
  1222. Args:
  1223. other: The other var to perform the logical AND with.
  1224. Returns:
  1225. A var representing the logical AND.
  1226. Note:
  1227. This method provides behavior specific to JavaScript, where it returns the JavaScript
  1228. equivalent code (using the '&&' operator) of a logical AND operation.
  1229. In JavaScript, the
  1230. logical OR operator '&&' is used for Boolean logic, and this method emulates that behavior
  1231. by returning the equivalent code as a Var instance.
  1232. In Python, logical AND 'and' operates differently, evaluating expressions immediately, making
  1233. it challenging to override the behavior entirely.
  1234. Therefore, this method leverages the
  1235. bitwise AND '__and__' operator for custom JavaScript-like behavior.
  1236. Example:
  1237. >>> var1 = Var.create(True)
  1238. >>> var2 = Var.create(False)
  1239. >>> js_code = var1 & var2
  1240. >>> print(js_code._var_full_name)
  1241. '(true && false)'
  1242. """
  1243. return self.operation("&&", other, type_=bool)
  1244. def __rand__(self, other: Var) -> Var:
  1245. """Perform a logical and.
  1246. Args:
  1247. other: The other var to perform the logical AND with.
  1248. Returns:
  1249. A var representing the logical AND.
  1250. Note:
  1251. This method provides behavior specific to JavaScript, where it returns the JavaScript
  1252. equivalent code (using the '&&' operator) of a logical AND operation.
  1253. In JavaScript, the
  1254. logical OR operator '&&' is used for Boolean logic, and this method emulates that behavior
  1255. by returning the equivalent code as a Var instance.
  1256. In Python, logical AND 'and' operates differently, evaluating expressions immediately, making
  1257. it challenging to override the behavior entirely.
  1258. Therefore, this method leverages the
  1259. bitwise AND '__rand__' operator for custom JavaScript-like behavior.
  1260. Example:
  1261. >>> var1 = Var.create(True)
  1262. >>> var2 = Var.create(False)
  1263. >>> js_code = var1 & var2
  1264. >>> print(js_code._var_full_name)
  1265. '(false && true)'
  1266. """
  1267. return self.operation("&&", other, type_=bool, flip=True)
  1268. def __or__(self, other: Var) -> Var:
  1269. """Perform a logical or.
  1270. Args:
  1271. other: The other var to perform the logical or with.
  1272. Returns:
  1273. A var representing the logical or.
  1274. Note:
  1275. This method provides behavior specific to JavaScript, where it returns the JavaScript
  1276. equivalent code (using the '||' operator) of a logical OR operation. In JavaScript, the
  1277. logical OR operator '||' is used for Boolean logic, and this method emulates that behavior
  1278. by returning the equivalent code as a Var instance.
  1279. In Python, logical OR 'or' operates differently, evaluating expressions immediately, making
  1280. it challenging to override the behavior entirely. Therefore, this method leverages the
  1281. bitwise OR '__or__' operator for custom JavaScript-like behavior.
  1282. Example:
  1283. >>> var1 = Var.create(True)
  1284. >>> var2 = Var.create(False)
  1285. >>> js_code = var1 | var2
  1286. >>> print(js_code._var_full_name)
  1287. '(true || false)'
  1288. """
  1289. return self.operation("||", other, type_=bool)
  1290. def __ror__(self, other: Var) -> Var:
  1291. """Perform a logical or.
  1292. Args:
  1293. other: The other var to perform the logical or with.
  1294. Returns:
  1295. A var representing the logical or.
  1296. Note:
  1297. This method provides behavior specific to JavaScript, where it returns the JavaScript
  1298. equivalent code (using the '||' operator) of a logical OR operation. In JavaScript, the
  1299. logical OR operator '||' is used for Boolean logic, and this method emulates that behavior
  1300. by returning the equivalent code as a Var instance.
  1301. In Python, logical OR 'or' operates differently, evaluating expressions immediately, making
  1302. it challenging to override the behavior entirely. Therefore, this method leverages the
  1303. bitwise OR '__or__' operator for custom JavaScript-like behavior.
  1304. Example:
  1305. >>> var1 = Var.create(True)
  1306. >>> var2 = Var.create(False)
  1307. >>> js_code = var1 | var2
  1308. >>> print(js_code)
  1309. 'false || true'
  1310. """
  1311. return self.operation("||", other, type_=bool, flip=True)
  1312. def __contains__(self, _: Any) -> Var:
  1313. """Override the 'in' operator to alert the user that it is not supported.
  1314. Raises:
  1315. VarTypeError: the operation is not supported
  1316. """
  1317. raise VarTypeError(
  1318. "'in' operator not supported for Var types, use Var.contains() instead."
  1319. )
  1320. def contains(self, other: Any, field: Union[Var, None] = None) -> Var:
  1321. """Check if a var contains the object `other`.
  1322. Args:
  1323. other: The object to check.
  1324. field: Optionally specify a field to check on both object and the other var.
  1325. Raises:
  1326. VarTypeError: If the var is not a valid type: dict, list, tuple or str.
  1327. Returns:
  1328. A var representing the contain check.
  1329. """
  1330. if not (types._issubclass(self._var_type, Union[dict, list, tuple, str, set])):
  1331. raise VarTypeError(
  1332. f"Var {self._var_full_name} of type {self._var_type} does not support contains check."
  1333. )
  1334. method = (
  1335. "hasOwnProperty"
  1336. if types.get_base_class(self._var_type) == dict
  1337. else "includes"
  1338. )
  1339. if isinstance(other, str):
  1340. other = Var.create(json.dumps(other), _var_is_string=True)
  1341. elif not isinstance(other, Var):
  1342. other = Var.create(other, _var_is_string=False)
  1343. if types._issubclass(self._var_type, Dict):
  1344. return self._replace(
  1345. _var_name=f"{self._var_name}.{method}({other._var_full_name})",
  1346. _var_type=bool,
  1347. _var_is_string=False,
  1348. merge_var_data=other._var_data,
  1349. )
  1350. else: # str, list, tuple
  1351. # For strings, the left operand must be a string.
  1352. if types._issubclass(self._var_type, str) and not types._issubclass(
  1353. other._var_type, str
  1354. ):
  1355. raise VarTypeError(
  1356. f"'in <string>' requires string as left operand, not {other._var_type}"
  1357. )
  1358. _var_name = None
  1359. if field is None:
  1360. _var_name = f"{self._var_name}.includes({other._var_full_name})"
  1361. else:
  1362. field = Var.create_safe(field, _var_is_string=isinstance(field, str))
  1363. _var_name = f"{self._var_name}.some(e=>e[{field._var_name_unwrapped}]==={other._var_full_name})"
  1364. return self._replace(
  1365. _var_name=_var_name,
  1366. _var_type=bool,
  1367. _var_is_string=False,
  1368. merge_var_data=other._var_data,
  1369. )
  1370. def reverse(self) -> Var:
  1371. """Reverse a list var.
  1372. Raises:
  1373. VarTypeError: If the var is not a list.
  1374. Returns:
  1375. A var with the reversed list.
  1376. """
  1377. if not types._issubclass(self._var_type, list):
  1378. raise VarTypeError(f"Cannot reverse non-list var {self._var_full_name}.")
  1379. return self._replace(
  1380. _var_name=f"[...{self._var_full_name}].reverse()",
  1381. _var_is_string=False,
  1382. _var_full_name_needs_state_prefix=False,
  1383. )
  1384. def lower(self) -> Var:
  1385. """Convert a string var to lowercase.
  1386. Returns:
  1387. A var with the lowercase string.
  1388. Raises:
  1389. VarTypeError: If the var is not a string.
  1390. """
  1391. if not types._issubclass(self._var_type, str):
  1392. raise VarTypeError(
  1393. f"Cannot convert non-string var {self._var_full_name} to lowercase."
  1394. )
  1395. return self._replace(
  1396. _var_name=f"{self._var_name}.toLowerCase()",
  1397. _var_is_string=False,
  1398. _var_type=str,
  1399. )
  1400. def upper(self) -> Var:
  1401. """Convert a string var to uppercase.
  1402. Returns:
  1403. A var with the uppercase string.
  1404. Raises:
  1405. VarTypeError: If the var is not a string.
  1406. """
  1407. if not types._issubclass(self._var_type, str):
  1408. raise VarTypeError(
  1409. f"Cannot convert non-string var {self._var_full_name} to uppercase."
  1410. )
  1411. return self._replace(
  1412. _var_name=f"{self._var_name}.toUpperCase()",
  1413. _var_is_string=False,
  1414. _var_type=str,
  1415. )
  1416. def strip(self, other: str | Var[str] = " ") -> Var:
  1417. """Strip a string var.
  1418. Args:
  1419. other: The string to strip the var with.
  1420. Returns:
  1421. A var with the stripped string.
  1422. Raises:
  1423. VarTypeError: If the var is not a string.
  1424. """
  1425. if not types._issubclass(self._var_type, str):
  1426. raise VarTypeError(f"Cannot strip non-string var {self._var_full_name}.")
  1427. other = (
  1428. Var.create_safe(json.dumps(other), _var_is_string=False)
  1429. if isinstance(other, str)
  1430. else other
  1431. )
  1432. return self._replace(
  1433. _var_name=f"{self._var_name}.replace(/^${other._var_full_name}|${other._var_full_name}$/g, '')",
  1434. _var_is_string=False,
  1435. merge_var_data=other._var_data,
  1436. )
  1437. def split(self, other: str | Var[str] = " ") -> Var:
  1438. """Split a string var into a list.
  1439. Args:
  1440. other: The string to split the var with.
  1441. Returns:
  1442. A var with the list.
  1443. Raises:
  1444. VarTypeError: If the var is not a string.
  1445. """
  1446. if not types._issubclass(self._var_type, str):
  1447. raise VarTypeError(f"Cannot split non-string var {self._var_full_name}.")
  1448. other = (
  1449. Var.create_safe(json.dumps(other), _var_is_string=False)
  1450. if isinstance(other, str)
  1451. else other
  1452. )
  1453. return self._replace(
  1454. _var_name=f"{self._var_name}.split({other._var_full_name})",
  1455. _var_is_string=False,
  1456. _var_type=List[str],
  1457. merge_var_data=other._var_data,
  1458. )
  1459. def join(self, other: str | Var[str] | None = None) -> Var:
  1460. """Join a list var into a string.
  1461. Args:
  1462. other: The string to join the list with.
  1463. Returns:
  1464. A var with the string.
  1465. Raises:
  1466. VarTypeError: If the var is not a list.
  1467. """
  1468. if not types._issubclass(self._var_type, list):
  1469. raise VarTypeError(f"Cannot join non-list var {self._var_full_name}.")
  1470. if other is None:
  1471. other = Var.create_safe('""', _var_is_string=False)
  1472. if isinstance(other, str):
  1473. other = Var.create_safe(json.dumps(other), _var_is_string=False)
  1474. else:
  1475. other = Var.create_safe(other, _var_is_string=False)
  1476. return self._replace(
  1477. _var_name=f"{self._var_name}.join({other._var_full_name})",
  1478. _var_is_string=False,
  1479. _var_type=str,
  1480. merge_var_data=other._var_data,
  1481. )
  1482. def foreach(self, fn: Callable) -> Var:
  1483. """Return a list of components. after doing a foreach on this var.
  1484. Args:
  1485. fn: The function to call on each component.
  1486. Returns:
  1487. A var representing foreach operation.
  1488. Raises:
  1489. VarTypeError: If the var is not a list.
  1490. """
  1491. inner_types = get_args(self._var_type)
  1492. if not inner_types:
  1493. raise VarTypeError(
  1494. f"Cannot foreach over non-sequence var {self._var_full_name} of type {self._var_type}."
  1495. )
  1496. arg = BaseVar(
  1497. _var_name=get_unique_variable_name(),
  1498. _var_type=inner_types[0],
  1499. )
  1500. index = BaseVar(
  1501. _var_name=get_unique_variable_name(),
  1502. _var_type=int,
  1503. )
  1504. fn_signature = inspect.signature(fn)
  1505. fn_args = (arg, index)
  1506. fn_ret = fn(*fn_args[: len(fn_signature.parameters)])
  1507. return self._replace(
  1508. _var_name=f"{self._var_full_name}.map(({arg._var_name}, {index._var_name}) => {fn_ret})",
  1509. _var_is_string=False,
  1510. )
  1511. @classmethod
  1512. def range(
  1513. cls,
  1514. v1: Var | int = 0,
  1515. v2: Var | int | None = None,
  1516. step: Var | int | None = None,
  1517. ) -> Var:
  1518. """Return an iterator over indices from v1 to v2 (or 0 to v1).
  1519. Args:
  1520. v1: The start of the range or end of range if v2 is not given.
  1521. v2: The end of the range.
  1522. step: The number of numbers between each item.
  1523. Returns:
  1524. A var representing range operation.
  1525. Raises:
  1526. VarTypeError: If the var is not an int.
  1527. """
  1528. if not isinstance(v1, Var):
  1529. v1 = Var.create_safe(v1)
  1530. if v1._var_type != int:
  1531. raise VarTypeError(f"Cannot get range on non-int var {v1._var_full_name}.")
  1532. if not isinstance(v2, Var):
  1533. v2 = Var.create(v2)
  1534. if v2 is None:
  1535. v2 = Var.create_safe("undefined", _var_is_string=False)
  1536. elif v2._var_type != int:
  1537. raise VarTypeError(f"Cannot get range on non-int var {v2._var_full_name}.")
  1538. if not isinstance(step, Var):
  1539. step = Var.create(step)
  1540. if step is None:
  1541. step = Var.create_safe(1)
  1542. elif step._var_type != int:
  1543. raise VarTypeError(
  1544. f"Cannot get range on non-int var {step._var_full_name}."
  1545. )
  1546. return BaseVar(
  1547. _var_name=f"Array.from(range({v1._var_full_name}, {v2._var_full_name}, {step._var_name}))",
  1548. _var_type=List[int],
  1549. _var_is_local=False,
  1550. _var_data=VarData.merge(
  1551. v1._var_data,
  1552. v2._var_data,
  1553. step._var_data,
  1554. VarData(
  1555. imports={
  1556. "/utils/helpers/range.js": [
  1557. ImportVar(tag="range", is_default=True),
  1558. ],
  1559. },
  1560. ),
  1561. ),
  1562. )
  1563. def to(self, type_: Type) -> Var:
  1564. """Convert the type of the var.
  1565. Args:
  1566. type_: The type to convert to.
  1567. Returns:
  1568. The converted var.
  1569. """
  1570. return self._replace(_var_type=type_)
  1571. def as_ref(self) -> Var:
  1572. """Convert the var to a ref.
  1573. Returns:
  1574. The var as a ref.
  1575. """
  1576. return self._replace(
  1577. _var_name=f"refs['{self._var_full_name}']",
  1578. _var_is_local=True,
  1579. _var_is_string=False,
  1580. _var_full_name_needs_state_prefix=False,
  1581. merge_var_data=VarData(
  1582. imports={
  1583. f"/{constants.Dirs.STATE_PATH}": [imports.ImportVar(tag="refs")],
  1584. },
  1585. ),
  1586. )
  1587. @property
  1588. def _var_full_name(self) -> str:
  1589. """Get the full name of the var.
  1590. Returns:
  1591. The full name of the var.
  1592. """
  1593. from reflex.utils import format
  1594. if not self._var_full_name_needs_state_prefix:
  1595. return self._var_name
  1596. return (
  1597. self._var_name
  1598. if self._var_data is None or self._var_data.state == ""
  1599. else ".".join(
  1600. [format.format_state_name(self._var_data.state), self._var_name]
  1601. )
  1602. )
  1603. def _var_set_state(self, state: Type[BaseState] | str) -> Any:
  1604. """Set the state of the var.
  1605. Args:
  1606. state: The state to set or the full name of the state.
  1607. Returns:
  1608. The var with the set state.
  1609. """
  1610. from reflex.utils import format
  1611. state_name = state if isinstance(state, str) else state.get_full_name()
  1612. new_var_data = VarData(
  1613. state=state_name,
  1614. hooks={
  1615. "const {0} = useContext(StateContexts.{0})".format(
  1616. format.format_state_name(state_name)
  1617. ): None
  1618. },
  1619. imports={
  1620. f"/{constants.Dirs.CONTEXTS_PATH}": [ImportVar(tag="StateContexts")],
  1621. "react": [ImportVar(tag="useContext")],
  1622. },
  1623. )
  1624. self._var_data = VarData.merge(self._var_data, new_var_data)
  1625. self._var_full_name_needs_state_prefix = True
  1626. return self
  1627. @property
  1628. def _var_state(self) -> str:
  1629. """Compat method for getting the state.
  1630. Returns:
  1631. The state name associated with the var.
  1632. """
  1633. return self._var_data.state if self._var_data else ""
  1634. def _get_all_var_data(self) -> VarData | None:
  1635. """Get all the var data.
  1636. Returns:
  1637. The var data.
  1638. """
  1639. return self._var_data
  1640. def json(self) -> str:
  1641. """Serialize the var to a JSON string.
  1642. Raises:
  1643. NotImplementedError: If the method is not implemented.
  1644. """
  1645. raise NotImplementedError("Var subclasses must implement the json method.")
  1646. @property
  1647. def _var_name_unwrapped(self) -> str:
  1648. """Get the var str without wrapping in curly braces.
  1649. Returns:
  1650. The str var without the wrapped curly braces
  1651. """
  1652. from reflex.style import Style
  1653. generic_alias = types.is_generic_alias(self._var_type)
  1654. type_ = get_origin(self._var_type) if generic_alias else self._var_type
  1655. wrapped_var = str(self)
  1656. return (
  1657. wrapped_var
  1658. if not self._var_state
  1659. and not generic_alias
  1660. and (types._issubclass(type_, dict) or types._issubclass(type_, Style))
  1661. else wrapped_var.strip("{}")
  1662. )
  1663. # Allow automatic serialization of Var within JSON structures
  1664. serializers.serializer(_encode_var)
  1665. @dataclasses.dataclass(
  1666. eq=False,
  1667. **{"slots": True} if sys.version_info >= (3, 10) else {},
  1668. )
  1669. class BaseVar(Var):
  1670. """A base (non-computed) var of the app state."""
  1671. # The name of the var.
  1672. _var_name: str = dataclasses.field()
  1673. # The type of the var.
  1674. _var_type: Type = dataclasses.field(default=Any)
  1675. # Whether this is a local javascript variable.
  1676. _var_is_local: bool = dataclasses.field(default=False)
  1677. # Whether the var is a string literal.
  1678. _var_is_string: bool = dataclasses.field(default=False)
  1679. # _var_full_name should be prefixed with _var_state
  1680. _var_full_name_needs_state_prefix: bool = dataclasses.field(default=False)
  1681. # Extra metadata associated with the Var
  1682. _var_data: Optional[VarData] = dataclasses.field(default=None)
  1683. def __hash__(self) -> int:
  1684. """Define a hash function for a var.
  1685. Returns:
  1686. The hash of the var.
  1687. """
  1688. return hash((self._var_name, str(self._var_type)))
  1689. def get_default_value(self) -> Any:
  1690. """Get the default value of the var.
  1691. Returns:
  1692. The default value of the var.
  1693. Raises:
  1694. ImportError: If the var is a dataframe and pandas is not installed.
  1695. """
  1696. if types.is_optional(self._var_type):
  1697. return None
  1698. type_ = (
  1699. get_origin(self._var_type)
  1700. if types.is_generic_alias(self._var_type)
  1701. else self._var_type
  1702. )
  1703. if type_ is Literal:
  1704. args = get_args(self._var_type)
  1705. return args[0] if args else None
  1706. if issubclass(type_, str):
  1707. return ""
  1708. if issubclass(type_, types.get_args(Union[int, float])):
  1709. return 0
  1710. if issubclass(type_, bool):
  1711. return False
  1712. if issubclass(type_, list):
  1713. return []
  1714. if issubclass(type_, dict):
  1715. return {}
  1716. if issubclass(type_, tuple):
  1717. return ()
  1718. if types.is_dataframe(type_):
  1719. try:
  1720. import pandas as pd
  1721. return pd.DataFrame()
  1722. except ImportError as e:
  1723. raise ImportError(
  1724. "Please install pandas to use dataframes in your app."
  1725. ) from e
  1726. return set() if issubclass(type_, set) else None
  1727. def get_setter_name(self, include_state: bool = True) -> str:
  1728. """Get the name of the var's generated setter function.
  1729. Args:
  1730. include_state: Whether to include the state name in the setter name.
  1731. Returns:
  1732. The name of the setter function.
  1733. """
  1734. setter = constants.SETTER_PREFIX + self._var_name
  1735. if self._var_data is None:
  1736. return setter
  1737. if not include_state or self._var_data.state == "":
  1738. return setter
  1739. return ".".join((self._var_data.state, setter))
  1740. def get_setter(self) -> Callable[[BaseState, Any], None]:
  1741. """Get the var's setter function.
  1742. Returns:
  1743. A function that that creates a setter for the var.
  1744. """
  1745. def setter(state: BaseState, value: Any):
  1746. """Get the setter for the var.
  1747. Args:
  1748. state: The state within which we add the setter function.
  1749. value: The value to set.
  1750. """
  1751. if self._var_type in [int, float]:
  1752. try:
  1753. value = self._var_type(value)
  1754. setattr(state, self._var_name, value)
  1755. except ValueError:
  1756. console.debug(
  1757. f"{type(state).__name__}.{self._var_name}: Failed conversion of {value} to '{self._var_type.__name__}'. Value not set.",
  1758. )
  1759. else:
  1760. setattr(state, self._var_name, value)
  1761. setter.__qualname__ = self.get_setter_name()
  1762. return setter
  1763. @dataclasses.dataclass(init=False, eq=False)
  1764. class ComputedVar(Var, property):
  1765. """A field with computed getters."""
  1766. # Whether to track dependencies and cache computed values
  1767. _cache: bool = dataclasses.field(default=False)
  1768. # Whether the computed var is a backend var
  1769. _backend: bool = dataclasses.field(default=False)
  1770. # The initial value of the computed var
  1771. _initial_value: Any | types.Unset = dataclasses.field(default=types.Unset())
  1772. # Explicit var dependencies to track
  1773. _static_deps: set[str] = dataclasses.field(default_factory=set)
  1774. # Whether var dependencies should be auto-determined
  1775. _auto_deps: bool = dataclasses.field(default=True)
  1776. # Interval at which the computed var should be updated
  1777. _update_interval: Optional[datetime.timedelta] = dataclasses.field(default=None)
  1778. # The name of the var.
  1779. _var_name: str = dataclasses.field()
  1780. # The type of the var.
  1781. _var_type: Type = dataclasses.field(default=Any)
  1782. # Whether this is a local javascript variable.
  1783. _var_is_local: bool = dataclasses.field(default=False)
  1784. # Whether the var is a string literal.
  1785. _var_is_string: bool = dataclasses.field(default=False)
  1786. # _var_full_name should be prefixed with _var_state
  1787. _var_full_name_needs_state_prefix: bool = dataclasses.field(default=False)
  1788. # Extra metadata associated with the Var
  1789. _var_data: Optional[VarData] = dataclasses.field(default=None)
  1790. def __init__(
  1791. self,
  1792. fget: Callable[[BaseState], Any],
  1793. initial_value: Any | types.Unset = types.Unset(),
  1794. cache: bool = False,
  1795. deps: Optional[List[Union[str, Var]]] = None,
  1796. auto_deps: bool = True,
  1797. interval: Optional[Union[int, datetime.timedelta]] = None,
  1798. backend: bool | None = None,
  1799. **kwargs,
  1800. ):
  1801. """Initialize a ComputedVar.
  1802. Args:
  1803. fget: The getter function.
  1804. initial_value: The initial value of the computed var.
  1805. cache: Whether to cache the computed value.
  1806. deps: Explicit var dependencies to track.
  1807. auto_deps: Whether var dependencies should be auto-determined.
  1808. interval: Interval at which the computed var should be updated.
  1809. backend: Whether the computed var is a backend var.
  1810. **kwargs: additional attributes to set on the instance
  1811. Raises:
  1812. TypeError: If the computed var dependencies are not Var instances or var names.
  1813. """
  1814. if backend is None:
  1815. backend = fget.__name__.startswith("_")
  1816. self._backend = backend
  1817. self._initial_value = initial_value
  1818. self._cache = cache
  1819. if isinstance(interval, int):
  1820. interval = datetime.timedelta(seconds=interval)
  1821. self._update_interval = interval
  1822. if deps is None:
  1823. deps = []
  1824. else:
  1825. for dep in deps:
  1826. if isinstance(dep, Var):
  1827. continue
  1828. if isinstance(dep, str) and dep != "":
  1829. continue
  1830. raise TypeError(
  1831. "ComputedVar dependencies must be Var instances or var names (non-empty strings)."
  1832. )
  1833. self._static_deps = {
  1834. dep._var_name if isinstance(dep, Var) else dep for dep in deps
  1835. }
  1836. self._auto_deps = auto_deps
  1837. property.__init__(self, fget)
  1838. kwargs["_var_name"] = kwargs.pop("_var_name", fget.__name__)
  1839. kwargs["_var_type"] = kwargs.pop("_var_type", self._determine_var_type())
  1840. BaseVar.__init__(self, **kwargs) # type: ignore
  1841. @override
  1842. def _replace(self, merge_var_data=None, **kwargs: Any) -> ComputedVar:
  1843. """Replace the attributes of the ComputedVar.
  1844. Args:
  1845. merge_var_data: VarData to merge into the existing VarData.
  1846. **kwargs: Var fields to update.
  1847. Returns:
  1848. The new ComputedVar instance.
  1849. Raises:
  1850. TypeError: If kwargs contains keys that are not allowed.
  1851. """
  1852. field_values = dict(
  1853. fget=kwargs.pop("fget", self.fget),
  1854. initial_value=kwargs.pop("initial_value", self._initial_value),
  1855. cache=kwargs.pop("cache", self._cache),
  1856. deps=kwargs.pop("deps", self._static_deps),
  1857. auto_deps=kwargs.pop("auto_deps", self._auto_deps),
  1858. interval=kwargs.pop("interval", self._update_interval),
  1859. backend=kwargs.pop("backend", self._backend),
  1860. _var_name=kwargs.pop("_var_name", self._var_name),
  1861. _var_type=kwargs.pop("_var_type", self._var_type),
  1862. _var_is_local=kwargs.pop("_var_is_local", self._var_is_local),
  1863. _var_is_string=kwargs.pop("_var_is_string", self._var_is_string),
  1864. _var_full_name_needs_state_prefix=kwargs.pop(
  1865. "_var_full_name_needs_state_prefix",
  1866. self._var_full_name_needs_state_prefix,
  1867. ),
  1868. _var_data=VarData.merge(self._var_data, merge_var_data),
  1869. )
  1870. if kwargs:
  1871. unexpected_kwargs = ", ".join(kwargs.keys())
  1872. raise TypeError(f"Unexpected keyword arguments: {unexpected_kwargs}")
  1873. return ComputedVar(**field_values)
  1874. @property
  1875. def _cache_attr(self) -> str:
  1876. """Get the attribute used to cache the value on the instance.
  1877. Returns:
  1878. An attribute name.
  1879. """
  1880. return f"__cached_{self._var_name}"
  1881. @property
  1882. def _last_updated_attr(self) -> str:
  1883. """Get the attribute used to store the last updated timestamp.
  1884. Returns:
  1885. An attribute name.
  1886. """
  1887. return f"__last_updated_{self._var_name}"
  1888. def needs_update(self, instance: BaseState) -> bool:
  1889. """Check if the computed var needs to be updated.
  1890. Args:
  1891. instance: The state instance that the computed var is attached to.
  1892. Returns:
  1893. True if the computed var needs to be updated, False otherwise.
  1894. """
  1895. if self._update_interval is None:
  1896. return False
  1897. last_updated = getattr(instance, self._last_updated_attr, None)
  1898. if last_updated is None:
  1899. return True
  1900. return datetime.datetime.now() - last_updated > self._update_interval
  1901. def __get__(self, instance: BaseState | None, owner):
  1902. """Get the ComputedVar value.
  1903. If the value is already cached on the instance, return the cached value.
  1904. Args:
  1905. instance: the instance of the class accessing this computed var.
  1906. owner: the class that this descriptor is attached to.
  1907. Returns:
  1908. The value of the var for the given instance.
  1909. """
  1910. if instance is None or not self._cache:
  1911. return super().__get__(instance, owner)
  1912. # handle caching
  1913. if not hasattr(instance, self._cache_attr) or self.needs_update(instance):
  1914. # Set cache attr on state instance.
  1915. setattr(instance, self._cache_attr, super().__get__(instance, owner))
  1916. # Ensure the computed var gets serialized to redis.
  1917. instance._was_touched = True
  1918. # Set the last updated timestamp on the state instance.
  1919. setattr(instance, self._last_updated_attr, datetime.datetime.now())
  1920. return getattr(instance, self._cache_attr)
  1921. def _deps(
  1922. self,
  1923. objclass: Type,
  1924. obj: FunctionType | CodeType | None = None,
  1925. self_name: Optional[str] = None,
  1926. ) -> set[str]:
  1927. """Determine var dependencies of this ComputedVar.
  1928. Save references to attributes accessed on "self". Recursively called
  1929. when the function makes a method call on "self" or define comprehensions
  1930. or nested functions that may reference "self".
  1931. Args:
  1932. objclass: the class obj this ComputedVar is attached to.
  1933. obj: the object to disassemble (defaults to the fget function).
  1934. self_name: if specified, look for this name in LOAD_FAST and LOAD_DEREF instructions.
  1935. Returns:
  1936. A set of variable names accessed by the given obj.
  1937. Raises:
  1938. VarValueError: if the function references the get_state, parent_state, or substates attributes
  1939. (cannot track deps in a related state, only implicitly via parent state).
  1940. """
  1941. if not self._auto_deps:
  1942. return self._static_deps
  1943. d = self._static_deps.copy()
  1944. if obj is None:
  1945. fget = property.__getattribute__(self, "fget")
  1946. if fget is not None:
  1947. obj = cast(FunctionType, fget)
  1948. else:
  1949. return set()
  1950. with contextlib.suppress(AttributeError):
  1951. # unbox functools.partial
  1952. obj = cast(FunctionType, obj.func) # type: ignore
  1953. with contextlib.suppress(AttributeError):
  1954. # unbox EventHandler
  1955. obj = cast(FunctionType, obj.fn) # type: ignore
  1956. if self_name is None and isinstance(obj, FunctionType):
  1957. try:
  1958. # the first argument to the function is the name of "self" arg
  1959. self_name = obj.__code__.co_varnames[0]
  1960. except (AttributeError, IndexError):
  1961. self_name = None
  1962. if self_name is None:
  1963. # cannot reference attributes on self if method takes no args
  1964. return set()
  1965. invalid_names = ["get_state", "parent_state", "substates", "get_substate"]
  1966. self_is_top_of_stack = False
  1967. for instruction in dis.get_instructions(obj):
  1968. if (
  1969. instruction.opname in ("LOAD_FAST", "LOAD_DEREF")
  1970. and instruction.argval == self_name
  1971. ):
  1972. # bytecode loaded the class instance to the top of stack, next load instruction
  1973. # is referencing an attribute on self
  1974. self_is_top_of_stack = True
  1975. continue
  1976. if self_is_top_of_stack and instruction.opname in (
  1977. "LOAD_ATTR",
  1978. "LOAD_METHOD",
  1979. ):
  1980. try:
  1981. ref_obj = getattr(objclass, instruction.argval)
  1982. except Exception:
  1983. ref_obj = None
  1984. if instruction.argval in invalid_names:
  1985. raise VarValueError(
  1986. f"Cached var {self._var_full_name} cannot access arbitrary state via `{instruction.argval}`."
  1987. )
  1988. if callable(ref_obj):
  1989. # recurse into callable attributes
  1990. d.update(
  1991. self._deps(
  1992. objclass=objclass,
  1993. obj=ref_obj,
  1994. )
  1995. )
  1996. # recurse into property fget functions
  1997. elif isinstance(ref_obj, property) and not isinstance(
  1998. ref_obj, ComputedVar
  1999. ):
  2000. d.update(
  2001. self._deps(
  2002. objclass=objclass,
  2003. obj=ref_obj.fget, # type: ignore
  2004. )
  2005. )
  2006. elif (
  2007. instruction.argval in objclass.backend_vars
  2008. or instruction.argval in objclass.vars
  2009. ):
  2010. # var access
  2011. d.add(instruction.argval)
  2012. elif instruction.opname == "LOAD_CONST" and isinstance(
  2013. instruction.argval, CodeType
  2014. ):
  2015. # recurse into nested functions / comprehensions, which can reference
  2016. # instance attributes from the outer scope
  2017. d.update(
  2018. self._deps(
  2019. objclass=objclass,
  2020. obj=instruction.argval,
  2021. self_name=self_name,
  2022. )
  2023. )
  2024. self_is_top_of_stack = False
  2025. return d
  2026. def mark_dirty(self, instance) -> None:
  2027. """Mark this ComputedVar as dirty.
  2028. Args:
  2029. instance: the state instance that needs to recompute the value.
  2030. """
  2031. with contextlib.suppress(AttributeError):
  2032. delattr(instance, self._cache_attr)
  2033. def _determine_var_type(self) -> Type:
  2034. """Get the type of the var.
  2035. Returns:
  2036. The type of the var.
  2037. """
  2038. hints = get_type_hints(property.__getattribute__(self, "fget"))
  2039. if "return" in hints:
  2040. return hints["return"]
  2041. return Any
  2042. def computed_var(
  2043. fget: Callable[[BaseState], Any] | None = None,
  2044. initial_value: Any | types.Unset = types.Unset(),
  2045. cache: bool = False,
  2046. deps: Optional[List[Union[str, Var]]] = None,
  2047. auto_deps: bool = True,
  2048. interval: Optional[Union[datetime.timedelta, int]] = None,
  2049. backend: bool | None = None,
  2050. _deprecated_cached_var: bool = False,
  2051. **kwargs,
  2052. ) -> ComputedVar | Callable[[Callable[[BaseState], Any]], ComputedVar]:
  2053. """A ComputedVar decorator with or without kwargs.
  2054. Args:
  2055. fget: The getter function.
  2056. initial_value: The initial value of the computed var.
  2057. cache: Whether to cache the computed value.
  2058. deps: Explicit var dependencies to track.
  2059. auto_deps: Whether var dependencies should be auto-determined.
  2060. interval: Interval at which the computed var should be updated.
  2061. backend: Whether the computed var is a backend var.
  2062. _deprecated_cached_var: Indicate usage of deprecated cached_var partial function.
  2063. **kwargs: additional attributes to set on the instance
  2064. Returns:
  2065. A ComputedVar instance.
  2066. Raises:
  2067. ValueError: If caching is disabled and an update interval is set.
  2068. VarDependencyError: If user supplies dependencies without caching.
  2069. """
  2070. if _deprecated_cached_var:
  2071. console.deprecate(
  2072. feature_name="cached_var",
  2073. reason=("Use @rx.var(cache=True) instead of @rx.cached_var."),
  2074. deprecation_version="0.5.6",
  2075. removal_version="0.6.0",
  2076. )
  2077. if cache is False and interval is not None:
  2078. raise ValueError("Cannot set update interval without caching.")
  2079. if cache is False and (deps is not None or auto_deps is False):
  2080. raise VarDependencyError("Cannot track dependencies without caching.")
  2081. if fget is not None:
  2082. return ComputedVar(fget=fget, cache=cache)
  2083. def wrapper(fget: Callable[[BaseState], Any]) -> ComputedVar:
  2084. return ComputedVar(
  2085. fget=fget,
  2086. initial_value=initial_value,
  2087. cache=cache,
  2088. deps=deps,
  2089. auto_deps=auto_deps,
  2090. interval=interval,
  2091. backend=backend,
  2092. **kwargs,
  2093. )
  2094. return wrapper
  2095. # Partial function of computed_var with cache=True
  2096. cached_var = functools.partial(computed_var, cache=True, _deprecated_cached_var=True)
  2097. class CallableVar(BaseVar):
  2098. """Decorate a Var-returning function to act as both a Var and a function.
  2099. This is used as a compatibility shim for replacing Var objects in the
  2100. API with functions that return a family of Var.
  2101. """
  2102. def __init__(self, fn: Callable[..., BaseVar]):
  2103. """Initialize a CallableVar.
  2104. Args:
  2105. fn: The function to decorate (must return Var)
  2106. """
  2107. self.fn = fn
  2108. default_var = fn()
  2109. super().__init__(**dataclasses.asdict(default_var))
  2110. def __call__(self, *args, **kwargs) -> BaseVar:
  2111. """Call the decorated function.
  2112. Args:
  2113. *args: The args to pass to the function.
  2114. **kwargs: The kwargs to pass to the function.
  2115. Returns:
  2116. The Var returned from calling the function.
  2117. """
  2118. return self.fn(*args, **kwargs)
  2119. def get_uuid_string_var() -> Var:
  2120. """Return a Var that generates a single memoized UUID via .web/utils/state.js.
  2121. useMemo with an empty dependency array ensures that the generated UUID is
  2122. consistent across re-renders of the component.
  2123. Returns:
  2124. A Var that generates a UUID at runtime.
  2125. """
  2126. from reflex.utils.imports import ImportVar
  2127. unique_uuid_var = get_unique_variable_name()
  2128. unique_uuid_var_data = VarData(
  2129. imports={
  2130. f"/{constants.Dirs.STATE_PATH}": {ImportVar(tag="generateUUID")}, # type: ignore
  2131. "react": "useMemo",
  2132. },
  2133. hooks={f"const {unique_uuid_var} = useMemo(generateUUID, [])": None},
  2134. )
  2135. return BaseVar(
  2136. _var_name=unique_uuid_var,
  2137. _var_type=str,
  2138. _var_data=unique_uuid_var_data,
  2139. )