base.py 102 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509
  1. """Collection of base classes."""
  2. from __future__ import annotations
  3. import contextlib
  4. import copy
  5. import dataclasses
  6. import datetime
  7. import functools
  8. import inspect
  9. import json
  10. import random
  11. import re
  12. import string
  13. import uuid
  14. import warnings
  15. from collections.abc import Callable, Coroutine, Iterable, Mapping, Sequence
  16. from decimal import Decimal
  17. from types import CodeType, FunctionType
  18. from typing import ( # noqa: UP035
  19. TYPE_CHECKING,
  20. Any,
  21. ClassVar,
  22. Dict,
  23. FrozenSet,
  24. Generic,
  25. List,
  26. Literal,
  27. NoReturn,
  28. ParamSpec,
  29. Protocol,
  30. Set,
  31. Tuple,
  32. TypeGuard,
  33. TypeVar,
  34. cast,
  35. get_args,
  36. get_type_hints,
  37. overload,
  38. )
  39. from rich.markup import escape
  40. from typing_extensions import deprecated, override
  41. from reflex import constants
  42. from reflex.base import Base
  43. from reflex.constants.compiler import Hooks
  44. from reflex.utils import console, exceptions, imports, serializers, types
  45. from reflex.utils.exceptions import (
  46. ComputedVarSignatureError,
  47. UntypedComputedVarError,
  48. VarAttributeError,
  49. VarDependencyError,
  50. VarTypeError,
  51. )
  52. from reflex.utils.format import format_state_name
  53. from reflex.utils.imports import (
  54. ImmutableImportDict,
  55. ImmutableParsedImportDict,
  56. ImportDict,
  57. ImportVar,
  58. ParsedImportTuple,
  59. parse_imports,
  60. )
  61. from reflex.utils.types import (
  62. GenericType,
  63. Self,
  64. _isinstance,
  65. get_origin,
  66. has_args,
  67. safe_issubclass,
  68. unionize,
  69. )
  70. if TYPE_CHECKING:
  71. from reflex.components.component import BaseComponent
  72. from reflex.state import BaseState
  73. from .number import BooleanVar, LiteralBooleanVar, LiteralNumberVar, NumberVar
  74. from .object import LiteralObjectVar, ObjectVar
  75. from .sequence import ArrayVar, LiteralArrayVar, LiteralStringVar, StringVar
  76. VAR_TYPE = TypeVar("VAR_TYPE", covariant=True)
  77. OTHER_VAR_TYPE = TypeVar("OTHER_VAR_TYPE")
  78. STRING_T = TypeVar("STRING_T", bound=str)
  79. SEQUENCE_TYPE = TypeVar("SEQUENCE_TYPE", bound=Sequence)
  80. warnings.filterwarnings("ignore", message="fields may not start with an underscore")
  81. _PYDANTIC_VALIDATE_VALUES = "__pydantic_validate_values__"
  82. def _pydantic_validator(*args, **kwargs):
  83. return None
  84. @dataclasses.dataclass(
  85. eq=False,
  86. frozen=True,
  87. )
  88. class VarSubclassEntry:
  89. """Entry for a Var subclass."""
  90. var_subclass: type[Var]
  91. to_var_subclass: type[ToOperation]
  92. python_types: tuple[GenericType, ...]
  93. _var_subclasses: list[VarSubclassEntry] = []
  94. _var_literal_subclasses: list[tuple[type[LiteralVar], VarSubclassEntry]] = []
  95. @dataclasses.dataclass(
  96. eq=True,
  97. frozen=True,
  98. )
  99. class VarData:
  100. """Metadata associated with a x."""
  101. # The name of the enclosing state.
  102. state: str = dataclasses.field(default="")
  103. # The name of the field in the state.
  104. field_name: str = dataclasses.field(default="")
  105. # Imports needed to render this var
  106. imports: ParsedImportTuple = dataclasses.field(default_factory=tuple)
  107. # Hooks that need to be present in the component to render this var
  108. hooks: tuple[str, ...] = dataclasses.field(default_factory=tuple)
  109. # Dependencies of the var
  110. deps: tuple[Var, ...] = dataclasses.field(default_factory=tuple)
  111. # Position of the hook in the component
  112. position: Hooks.HookPosition | None = None
  113. # Components that are part of this var
  114. components: tuple[BaseComponent, ...] = dataclasses.field(default_factory=tuple)
  115. def __init__(
  116. self,
  117. state: str = "",
  118. field_name: str = "",
  119. imports: ImmutableImportDict | ImmutableParsedImportDict | None = None,
  120. hooks: Mapping[str, VarData | None] | Sequence[str] | str | None = None,
  121. deps: list[Var] | None = None,
  122. position: Hooks.HookPosition | None = None,
  123. components: Iterable[BaseComponent] | None = None,
  124. ):
  125. """Initialize the var data.
  126. Args:
  127. state: The name of the enclosing state.
  128. field_name: The name of the field in the state.
  129. imports: Imports needed to render this var.
  130. hooks: Hooks that need to be present in the component to render this var.
  131. deps: Dependencies of the var for useCallback.
  132. position: Position of the hook in the component.
  133. components: Components that are part of this var.
  134. """
  135. if isinstance(hooks, str):
  136. hooks = [hooks]
  137. if not isinstance(hooks, dict):
  138. hooks = dict.fromkeys(hooks or [])
  139. immutable_imports: ParsedImportTuple = tuple(
  140. (k, tuple(v)) for k, v in parse_imports(imports or {}).items()
  141. )
  142. object.__setattr__(self, "state", state)
  143. object.__setattr__(self, "field_name", field_name)
  144. object.__setattr__(self, "imports", immutable_imports)
  145. object.__setattr__(self, "hooks", tuple(hooks or {}))
  146. object.__setattr__(self, "deps", tuple(deps or []))
  147. object.__setattr__(self, "position", position or None)
  148. object.__setattr__(self, "components", tuple(components or []))
  149. if hooks and any(hooks.values()):
  150. # Merge our dependencies first, so they can be referenced.
  151. merged_var_data = VarData.merge(*hooks.values(), self)
  152. if merged_var_data is not None:
  153. object.__setattr__(self, "state", merged_var_data.state)
  154. object.__setattr__(self, "field_name", merged_var_data.field_name)
  155. object.__setattr__(self, "imports", merged_var_data.imports)
  156. object.__setattr__(self, "hooks", merged_var_data.hooks)
  157. object.__setattr__(self, "deps", merged_var_data.deps)
  158. object.__setattr__(self, "position", merged_var_data.position)
  159. object.__setattr__(self, "components", merged_var_data.components)
  160. def old_school_imports(self) -> ImportDict:
  161. """Return the imports as a mutable dict.
  162. Returns:
  163. The imports as a mutable dict.
  164. """
  165. return {k: list(v) for k, v in self.imports}
  166. def merge(*all: VarData | None) -> VarData | None:
  167. """Merge multiple var data objects.
  168. Args:
  169. *all: The var data objects to merge.
  170. Raises:
  171. ReflexError: If trying to merge VarData with different positions.
  172. Returns:
  173. The merged var data object.
  174. # noqa: DAR102 *all
  175. """
  176. all_var_datas = list(filter(None, all))
  177. if not all_var_datas:
  178. return None
  179. if len(all_var_datas) == 1:
  180. return all_var_datas[0]
  181. # Get the first non-empty field name or default to empty string.
  182. field_name = next(
  183. (var_data.field_name for var_data in all_var_datas if var_data.field_name),
  184. "",
  185. )
  186. # Get the first non-empty state or default to empty string.
  187. state = next(
  188. (var_data.state for var_data in all_var_datas if var_data.state), ""
  189. )
  190. hooks: dict[str, VarData | None] = {
  191. hook: None for var_data in all_var_datas for hook in var_data.hooks
  192. }
  193. _imports = imports.merge_imports(
  194. *(var_data.imports for var_data in all_var_datas)
  195. )
  196. deps = [dep for var_data in all_var_datas for dep in var_data.deps]
  197. positions = list(
  198. {
  199. var_data.position
  200. for var_data in all_var_datas
  201. if var_data.position is not None
  202. }
  203. )
  204. if positions:
  205. if len(positions) > 1:
  206. raise exceptions.ReflexError(
  207. f"Cannot merge var data with different positions: {positions}"
  208. )
  209. position = positions[0]
  210. else:
  211. position = None
  212. components = tuple(
  213. component for var_data in all_var_datas for component in var_data.components
  214. )
  215. return VarData(
  216. state=state,
  217. field_name=field_name,
  218. imports=_imports,
  219. hooks=hooks,
  220. deps=deps,
  221. position=position,
  222. components=components,
  223. )
  224. def __bool__(self) -> bool:
  225. """Check if the var data is non-empty.
  226. Returns:
  227. True if any field is set to a non-default value.
  228. """
  229. return bool(
  230. self.state
  231. or self.imports
  232. or self.hooks
  233. or self.field_name
  234. or self.deps
  235. or self.position
  236. or self.components
  237. )
  238. @classmethod
  239. def from_state(cls, state: type[BaseState] | str, field_name: str = "") -> VarData:
  240. """Set the state of the var.
  241. Args:
  242. state: The state to set or the full name of the state.
  243. field_name: The name of the field in the state. Optional.
  244. Returns:
  245. The var with the set state.
  246. """
  247. from reflex.utils import format
  248. state_name = state if isinstance(state, str) else state.get_full_name()
  249. return VarData(
  250. state=state_name,
  251. field_name=field_name,
  252. hooks={
  253. "const {0} = useContext(StateContexts.{0})".format(
  254. format.format_state_name(state_name)
  255. ): None
  256. },
  257. imports={
  258. f"$/{constants.Dirs.CONTEXTS_PATH}": [ImportVar(tag="StateContexts")],
  259. "react": [ImportVar(tag="useContext")],
  260. },
  261. )
  262. def _decode_var_immutable(value: str) -> tuple[VarData | None, str]:
  263. """Decode the state name from a formatted var.
  264. Args:
  265. value: The value to extract the state name from.
  266. Returns:
  267. The extracted state name and the value without the state name.
  268. """
  269. var_datas = []
  270. if isinstance(value, str):
  271. # fast path if there is no encoded VarData
  272. if constants.REFLEX_VAR_OPENING_TAG not in value:
  273. return None, value
  274. offset = 0
  275. # Find all tags.
  276. while m := _decode_var_pattern.search(value):
  277. start, end = m.span()
  278. value = value[:start] + value[end:]
  279. serialized_data = m.group(1)
  280. if serialized_data.isnumeric() or (
  281. serialized_data[0] == "-" and serialized_data[1:].isnumeric()
  282. ):
  283. # This is a global immutable var.
  284. var = _global_vars[int(serialized_data)]
  285. var_data = var._get_all_var_data()
  286. if var_data is not None:
  287. var_datas.append(var_data)
  288. offset += end - start
  289. return VarData.merge(*var_datas) if var_datas else None, value
  290. def can_use_in_object_var(cls: GenericType) -> bool:
  291. """Check if the class can be used in an ObjectVar.
  292. Args:
  293. cls: The class to check.
  294. Returns:
  295. Whether the class can be used in an ObjectVar.
  296. """
  297. if types.is_union(cls):
  298. return all(can_use_in_object_var(t) for t in types.get_args(cls))
  299. return (
  300. inspect.isclass(cls)
  301. and not safe_issubclass(cls, Var)
  302. and serializers.can_serialize(cls, dict)
  303. )
  304. class MetaclassVar(type):
  305. """Metaclass for the Var class."""
  306. def __setattr__(cls, name: str, value: Any):
  307. """Set an attribute on the class.
  308. Args:
  309. name: The name of the attribute.
  310. value: The value of the attribute.
  311. """
  312. super().__setattr__(
  313. name, value if name != _PYDANTIC_VALIDATE_VALUES else _pydantic_validator
  314. )
  315. @dataclasses.dataclass(
  316. eq=False,
  317. frozen=True,
  318. )
  319. class Var(Generic[VAR_TYPE], metaclass=MetaclassVar):
  320. """Base class for immutable vars."""
  321. # The name of the var.
  322. _js_expr: str = dataclasses.field()
  323. # The type of the var.
  324. _var_type: types.GenericType = dataclasses.field(default=Any)
  325. # Extra metadata associated with the Var
  326. _var_data: VarData | None = dataclasses.field(default=None)
  327. def __str__(self) -> str:
  328. """String representation of the var. Guaranteed to be a valid Javascript expression.
  329. Returns:
  330. The name of the var.
  331. """
  332. return self._js_expr
  333. @property
  334. def _var_is_local(self) -> bool:
  335. """Whether this is a local javascript variable.
  336. Returns:
  337. False
  338. """
  339. return False
  340. @property
  341. @deprecated("Use `_js_expr` instead.")
  342. def _var_name(self) -> str:
  343. """The name of the var.
  344. Returns:
  345. The name of the var.
  346. """
  347. return self._js_expr
  348. @property
  349. def _var_field_name(self) -> str:
  350. """The name of the field.
  351. Returns:
  352. The name of the field.
  353. """
  354. var_data = self._get_all_var_data()
  355. field_name = var_data.field_name if var_data else None
  356. return field_name or self._js_expr
  357. @property
  358. @deprecated("Use `_js_expr` instead.")
  359. def _var_name_unwrapped(self) -> str:
  360. """The name of the var without extra curly braces.
  361. Returns:
  362. The name of the var.
  363. """
  364. return self._js_expr
  365. @property
  366. def _var_is_string(self) -> bool:
  367. """Whether the var is a string literal.
  368. Returns:
  369. False
  370. """
  371. return False
  372. def __init_subclass__(
  373. cls,
  374. python_types: tuple[GenericType, ...] | GenericType = types.Unset(),
  375. default_type: GenericType = types.Unset(),
  376. **kwargs,
  377. ):
  378. """Initialize the subclass.
  379. Args:
  380. python_types: The python types that the var represents.
  381. default_type: The default type of the var. Defaults to the first python type.
  382. **kwargs: Additional keyword arguments.
  383. """
  384. super().__init_subclass__(**kwargs)
  385. if python_types or default_type:
  386. python_types = (
  387. (python_types if isinstance(python_types, tuple) else (python_types,))
  388. if python_types
  389. else ()
  390. )
  391. default_type = default_type or (python_types[0] if python_types else Any)
  392. @dataclasses.dataclass(
  393. eq=False,
  394. frozen=True,
  395. slots=True,
  396. )
  397. class ToVarOperation(ToOperation, cls):
  398. """Base class of converting a var to another var type."""
  399. _original: Var = dataclasses.field(
  400. default=Var(_js_expr="null", _var_type=None),
  401. )
  402. _default_var_type: ClassVar[GenericType] = default_type
  403. new_to_var_operation_name = f"{cls.__name__.removesuffix('Var')}CastedVar"
  404. ToVarOperation.__qualname__ = (
  405. ToVarOperation.__qualname__.removesuffix(ToVarOperation.__name__)
  406. + new_to_var_operation_name
  407. )
  408. ToVarOperation.__name__ = new_to_var_operation_name
  409. _var_subclasses.append(VarSubclassEntry(cls, ToVarOperation, python_types))
  410. def __post_init__(self):
  411. """Post-initialize the var.
  412. Raises:
  413. TypeError: If _js_expr is not a string.
  414. """
  415. if not isinstance(self._js_expr, str):
  416. raise TypeError(
  417. f"Expected _js_expr to be a string, got value {self._js_expr!r} of type {type(self._js_expr).__name__}"
  418. )
  419. if self._var_data is not None and not isinstance(self._var_data, VarData):
  420. raise TypeError(
  421. f"Expected _var_data to be a VarData, got value {self._var_data!r} of type {type(self._var_data).__name__}"
  422. )
  423. # Decode any inline Var markup and apply it to the instance
  424. _var_data, _js_expr = _decode_var_immutable(self._js_expr)
  425. if _var_data or _js_expr != self._js_expr:
  426. self.__init__(
  427. _js_expr=_js_expr,
  428. _var_type=self._var_type,
  429. _var_data=VarData.merge(self._var_data, _var_data),
  430. )
  431. def __hash__(self) -> int:
  432. """Define a hash function for the var.
  433. Returns:
  434. The hash of the var.
  435. """
  436. return hash((self._js_expr, self._var_type, self._var_data))
  437. def _get_all_var_data(self) -> VarData | None:
  438. """Get all VarData associated with the Var.
  439. Returns:
  440. The VarData of the components and all of its children.
  441. """
  442. return self._var_data
  443. def equals(self, other: Var) -> bool:
  444. """Check if two vars are equal.
  445. Args:
  446. other: The other var to compare.
  447. Returns:
  448. Whether the vars are equal.
  449. """
  450. return (
  451. self._js_expr == other._js_expr
  452. and self._var_type == other._var_type
  453. and self._get_all_var_data() == other._get_all_var_data()
  454. )
  455. @overload
  456. def _replace(
  457. self,
  458. _var_type: type[OTHER_VAR_TYPE],
  459. merge_var_data: VarData | None = None,
  460. **kwargs: Any,
  461. ) -> Var[OTHER_VAR_TYPE]: ...
  462. @overload
  463. def _replace(
  464. self,
  465. _var_type: GenericType | None = None,
  466. merge_var_data: VarData | None = None,
  467. **kwargs: Any,
  468. ) -> Self: ...
  469. def _replace(
  470. self,
  471. _var_type: GenericType | None = None,
  472. merge_var_data: VarData | None = None,
  473. **kwargs: Any,
  474. ) -> Self | Var:
  475. """Make a copy of this Var with updated fields.
  476. Args:
  477. _var_type: The new type of the Var.
  478. merge_var_data: VarData to merge into the existing VarData.
  479. **kwargs: Var fields to update.
  480. Returns:
  481. A new Var with the updated fields overwriting the corresponding fields in this Var.
  482. Raises:
  483. TypeError: If _var_is_local, _var_is_string, or _var_full_name_needs_state_prefix is not None.
  484. """
  485. if kwargs.get("_var_is_local", False) is not False:
  486. raise TypeError("The _var_is_local argument is not supported for Var.")
  487. if kwargs.get("_var_is_string", False) is not False:
  488. raise TypeError("The _var_is_string argument is not supported for Var.")
  489. if kwargs.get("_var_full_name_needs_state_prefix", False) is not False:
  490. raise TypeError(
  491. "The _var_full_name_needs_state_prefix argument is not supported for Var."
  492. )
  493. value_with_replaced = dataclasses.replace(
  494. self,
  495. _var_type=_var_type or self._var_type,
  496. _var_data=VarData.merge(
  497. kwargs.get("_var_data", self._var_data), merge_var_data
  498. ),
  499. **kwargs,
  500. )
  501. if (js_expr := kwargs.get("_js_expr")) is not None:
  502. object.__setattr__(value_with_replaced, "_js_expr", js_expr)
  503. return value_with_replaced
  504. @overload
  505. @classmethod
  506. def create( # pyright: ignore[reportOverlappingOverload]
  507. cls,
  508. value: NoReturn,
  509. _var_data: VarData | None = None,
  510. ) -> Var[Any]: ...
  511. @overload
  512. @classmethod
  513. def create( # pyright: ignore[reportOverlappingOverload]
  514. cls,
  515. value: bool,
  516. _var_data: VarData | None = None,
  517. ) -> LiteralBooleanVar: ...
  518. @overload
  519. @classmethod
  520. def create(
  521. cls,
  522. value: int,
  523. _var_data: VarData | None = None,
  524. ) -> LiteralNumberVar[int]: ...
  525. @overload
  526. @classmethod
  527. def create(
  528. cls,
  529. value: float,
  530. _var_data: VarData | None = None,
  531. ) -> LiteralNumberVar[float]: ...
  532. @overload
  533. @classmethod
  534. def create(
  535. cls,
  536. value: Decimal,
  537. _var_data: VarData | None = None,
  538. ) -> LiteralNumberVar[Decimal]: ...
  539. @overload
  540. @classmethod
  541. def create( # pyright: ignore [reportOverlappingOverload]
  542. cls,
  543. value: str,
  544. _var_data: VarData | None = None,
  545. ) -> LiteralStringVar: ...
  546. @overload
  547. @classmethod
  548. def create( # pyright: ignore [reportOverlappingOverload]
  549. cls,
  550. value: STRING_T,
  551. _var_data: VarData | None = None,
  552. ) -> StringVar[STRING_T]: ...
  553. @overload
  554. @classmethod
  555. def create( # pyright: ignore[reportOverlappingOverload]
  556. cls,
  557. value: None,
  558. _var_data: VarData | None = None,
  559. ) -> LiteralNoneVar: ...
  560. @overload
  561. @classmethod
  562. def create(
  563. cls,
  564. value: MAPPING_TYPE,
  565. _var_data: VarData | None = None,
  566. ) -> LiteralObjectVar[MAPPING_TYPE]: ...
  567. @overload
  568. @classmethod
  569. def create(
  570. cls,
  571. value: SEQUENCE_TYPE,
  572. _var_data: VarData | None = None,
  573. ) -> LiteralArrayVar[SEQUENCE_TYPE]: ...
  574. @overload
  575. @classmethod
  576. def create(
  577. cls,
  578. value: OTHER_VAR_TYPE,
  579. _var_data: VarData | None = None,
  580. ) -> Var[OTHER_VAR_TYPE]: ...
  581. @classmethod
  582. def create(
  583. cls,
  584. value: OTHER_VAR_TYPE,
  585. _var_data: VarData | None = None,
  586. ) -> Var[OTHER_VAR_TYPE]:
  587. """Create a var from a value.
  588. Args:
  589. value: The value to create the var from.
  590. _var_data: Additional hooks and imports associated with the Var.
  591. Returns:
  592. The var.
  593. """
  594. # If the value is already a var, do nothing.
  595. if isinstance(value, Var):
  596. return value
  597. return LiteralVar.create(value, _var_data=_var_data)
  598. @classmethod
  599. @deprecated("Use `.create()` instead.")
  600. def create_safe(
  601. cls,
  602. *args: Any,
  603. **kwargs: Any,
  604. ) -> Var:
  605. """Create a var from a value.
  606. Args:
  607. *args: The arguments to create the var from.
  608. **kwargs: The keyword arguments to create the var from.
  609. Returns:
  610. The var.
  611. """
  612. return cls.create(*args, **kwargs)
  613. def __format__(self, format_spec: str) -> str:
  614. """Format the var into a Javascript equivalent to an f-string.
  615. Args:
  616. format_spec: The format specifier (Ignored for now).
  617. Returns:
  618. The formatted var.
  619. """
  620. hashed_var = hash(self)
  621. _global_vars[hashed_var] = self
  622. # Encode the _var_data into the formatted output for tracking purposes.
  623. return f"{constants.REFLEX_VAR_OPENING_TAG}{hashed_var}{constants.REFLEX_VAR_CLOSING_TAG}{self._js_expr}"
  624. @overload
  625. def to(self, output: type[str]) -> StringVar: ...
  626. @overload
  627. def to(self, output: type[bool]) -> BooleanVar: ...
  628. @overload
  629. def to(self, output: type[int]) -> NumberVar[int]: ...
  630. @overload
  631. def to(self, output: type[float]) -> NumberVar[float]: ...
  632. @overload
  633. def to(self, output: type[Decimal]) -> NumberVar[Decimal]: ...
  634. @overload
  635. def to(
  636. self,
  637. output: type[list] | type[tuple] | type[set],
  638. ) -> ArrayVar: ...
  639. @overload
  640. def to(
  641. self,
  642. output: type[MAPPING_TYPE],
  643. ) -> ObjectVar[MAPPING_TYPE]: ...
  644. @overload
  645. def to(
  646. self, output: type[ObjectVar], var_type: type[VAR_INSIDE]
  647. ) -> ObjectVar[VAR_INSIDE]: ...
  648. @overload
  649. def to(
  650. self, output: type[ObjectVar], var_type: None = None
  651. ) -> ObjectVar[VAR_TYPE]: ...
  652. @overload
  653. def to(self, output: VAR_SUBCLASS, var_type: None = None) -> VAR_SUBCLASS: ...
  654. @overload
  655. def to(
  656. self,
  657. output: type[OUTPUT] | types.GenericType,
  658. var_type: types.GenericType | None = None,
  659. ) -> OUTPUT: ...
  660. def to(
  661. self,
  662. output: type[OUTPUT] | types.GenericType,
  663. var_type: types.GenericType | None = None,
  664. ) -> Var:
  665. """Convert the var to a different type.
  666. Args:
  667. output: The output type.
  668. var_type: The type of the var.
  669. Returns:
  670. The converted var.
  671. """
  672. from .object import ObjectVar
  673. fixed_output_type = get_origin(output) or output
  674. # If the first argument is a python type, we map it to the corresponding Var type.
  675. for var_subclass in _var_subclasses[::-1]:
  676. if fixed_output_type in var_subclass.python_types or safe_issubclass(
  677. fixed_output_type, var_subclass.python_types
  678. ):
  679. return self.to(var_subclass.var_subclass, output)
  680. if fixed_output_type is None:
  681. return get_to_operation(NoneVar).create(self) # pyright: ignore [reportReturnType]
  682. # Handle fixed_output_type being Base or a dataclass.
  683. if can_use_in_object_var(output):
  684. return self.to(ObjectVar, output)
  685. if inspect.isclass(output):
  686. for var_subclass in _var_subclasses[::-1]:
  687. if safe_issubclass(output, var_subclass.var_subclass):
  688. current_var_type = self._var_type
  689. if current_var_type is Any:
  690. new_var_type = var_type
  691. else:
  692. new_var_type = var_type or current_var_type
  693. to_operation_return = var_subclass.to_var_subclass.create(
  694. value=self, _var_type=new_var_type
  695. )
  696. return to_operation_return # pyright: ignore [reportReturnType]
  697. # If we can't determine the first argument, we just replace the _var_type.
  698. if not safe_issubclass(output, Var) or var_type is None:
  699. return dataclasses.replace(
  700. self,
  701. _var_type=output,
  702. )
  703. # We couldn't determine the output type to be any other Var type, so we replace the _var_type.
  704. if var_type is not None:
  705. return dataclasses.replace(
  706. self,
  707. _var_type=var_type,
  708. )
  709. return self
  710. @overload
  711. def guess_type(self: Var[NoReturn]) -> Var[Any]: ... # pyright: ignore [reportOverlappingOverload]
  712. @overload
  713. def guess_type(self: Var[str]) -> StringVar: ...
  714. @overload
  715. def guess_type(self: Var[bool]) -> BooleanVar: ...
  716. @overload
  717. def guess_type(self: Var[int] | Var[float] | Var[int | float]) -> NumberVar: ...
  718. @overload
  719. def guess_type(self: Var[BASE_TYPE]) -> ObjectVar[BASE_TYPE]: ...
  720. @overload
  721. def guess_type(self) -> Self: ...
  722. def guess_type(self) -> Var:
  723. """Guesses the type of the variable based on its `_var_type` attribute.
  724. Returns:
  725. Var: The guessed type of the variable.
  726. Raises:
  727. TypeError: If the type is not supported for guessing.
  728. """
  729. from .object import ObjectVar
  730. var_type = self._var_type
  731. if var_type is None:
  732. return self.to(None)
  733. if var_type is NoReturn:
  734. return self.to(Any)
  735. var_type = types.value_inside_optional(var_type)
  736. if var_type is Any:
  737. return self
  738. fixed_type = get_origin(var_type) or var_type
  739. if fixed_type in types.UnionTypes:
  740. inner_types = get_args(var_type)
  741. non_optional_inner_types = [
  742. types.value_inside_optional(inner_type) for inner_type in inner_types
  743. ]
  744. fixed_inner_types = [
  745. get_origin(inner_type) or inner_type
  746. for inner_type in non_optional_inner_types
  747. ]
  748. for var_subclass in _var_subclasses[::-1]:
  749. if all(
  750. safe_issubclass(t, var_subclass.python_types)
  751. for t in fixed_inner_types
  752. ):
  753. return self.to(var_subclass.var_subclass, self._var_type)
  754. if can_use_in_object_var(var_type):
  755. return self.to(ObjectVar, self._var_type)
  756. return self
  757. if fixed_type is Literal:
  758. args = get_args(var_type)
  759. fixed_type = unionize(*(type(arg) for arg in args))
  760. if not inspect.isclass(fixed_type):
  761. raise TypeError(f"Unsupported type {var_type} for guess_type.")
  762. if fixed_type is None:
  763. return self.to(None)
  764. for var_subclass in _var_subclasses[::-1]:
  765. if safe_issubclass(fixed_type, var_subclass.python_types):
  766. return self.to(var_subclass.var_subclass, self._var_type)
  767. if can_use_in_object_var(fixed_type):
  768. return self.to(ObjectVar, self._var_type)
  769. return self
  770. def _get_default_value(self) -> Any:
  771. """Get the default value of the var.
  772. Returns:
  773. The default value of the var.
  774. Raises:
  775. ImportError: If the var is a dataframe and pandas is not installed.
  776. """
  777. if types.is_optional(self._var_type):
  778. return None
  779. type_ = (
  780. get_origin(self._var_type)
  781. if types.is_generic_alias(self._var_type)
  782. else self._var_type
  783. )
  784. if type_ is Literal:
  785. args = get_args(self._var_type)
  786. return args[0] if args else None
  787. if safe_issubclass(type_, str):
  788. return ""
  789. if safe_issubclass(type_, types.get_args(int | float)):
  790. return 0
  791. if safe_issubclass(type_, bool):
  792. return False
  793. if safe_issubclass(type_, list):
  794. return []
  795. if safe_issubclass(type_, Mapping):
  796. return {}
  797. if safe_issubclass(type_, tuple):
  798. return ()
  799. if types.is_dataframe(type_):
  800. try:
  801. import pandas as pd
  802. return pd.DataFrame()
  803. except ImportError as e:
  804. raise ImportError(
  805. "Please install pandas to use dataframes in your app."
  806. ) from e
  807. return set() if safe_issubclass(type_, set) else None
  808. def _get_setter_name(self, include_state: bool = True) -> str:
  809. """Get the name of the var's generated setter function.
  810. Args:
  811. include_state: Whether to include the state name in the setter name.
  812. Returns:
  813. The name of the setter function.
  814. """
  815. setter = constants.SETTER_PREFIX + self._var_field_name
  816. var_data = self._get_all_var_data()
  817. if var_data is None:
  818. return setter
  819. if not include_state or var_data.state == "":
  820. return setter
  821. return ".".join((var_data.state, setter))
  822. def _get_setter(self) -> Callable[[BaseState, Any], None]:
  823. """Get the var's setter function.
  824. Returns:
  825. A function that that creates a setter for the var.
  826. """
  827. actual_name = self._var_field_name
  828. def setter(state: Any, value: Any):
  829. """Get the setter for the var.
  830. Args:
  831. state: The state within which we add the setter function.
  832. value: The value to set.
  833. """
  834. if self._var_type in [int, float]:
  835. try:
  836. value = self._var_type(value)
  837. setattr(state, actual_name, value)
  838. except ValueError:
  839. console.debug(
  840. f"{type(state).__name__}.{self._js_expr}: Failed conversion of {value!s} to '{self._var_type.__name__}'. Value not set.",
  841. )
  842. else:
  843. setattr(state, actual_name, value)
  844. setter.__annotations__["value"] = self._var_type
  845. setter.__qualname__ = self._get_setter_name()
  846. return setter
  847. def _var_set_state(self, state: type[BaseState] | str) -> Self:
  848. """Set the state of the var.
  849. Args:
  850. state: The state to set.
  851. Returns:
  852. The var with the state set.
  853. """
  854. formatted_state_name = (
  855. state
  856. if isinstance(state, str)
  857. else format_state_name(state.get_full_name())
  858. )
  859. return StateOperation.create( # pyright: ignore [reportReturnType]
  860. formatted_state_name,
  861. self,
  862. _var_data=VarData.merge(
  863. VarData.from_state(state, self._js_expr), self._var_data
  864. ),
  865. ).guess_type()
  866. def __eq__(self, other: Var | Any) -> BooleanVar:
  867. """Check if the current variable is equal to the given variable.
  868. Args:
  869. other (Var | Any): The variable to compare with.
  870. Returns:
  871. BooleanVar: A BooleanVar object representing the result of the equality check.
  872. """
  873. from .number import equal_operation
  874. return equal_operation(self, other)
  875. def __ne__(self, other: Var | Any) -> BooleanVar:
  876. """Check if the current object is not equal to the given object.
  877. Parameters:
  878. other (Var | Any): The object to compare with.
  879. Returns:
  880. BooleanVar: A BooleanVar object representing the result of the comparison.
  881. """
  882. from .number import equal_operation
  883. return ~equal_operation(self, other)
  884. def bool(self) -> BooleanVar:
  885. """Convert the var to a boolean.
  886. Returns:
  887. The boolean var.
  888. """
  889. from .number import boolify
  890. return boolify(self)
  891. def is_none(self) -> BooleanVar:
  892. """Check if the var is None.
  893. Returns:
  894. A BooleanVar object representing the result of the check.
  895. """
  896. from .number import is_not_none_operation
  897. return ~is_not_none_operation(self)
  898. def is_not_none(self) -> BooleanVar:
  899. """Check if the var is not None.
  900. Returns:
  901. A BooleanVar object representing the result of the check.
  902. """
  903. from .number import is_not_none_operation
  904. return is_not_none_operation(self)
  905. def __and__(
  906. self, other: Var[OTHER_VAR_TYPE] | Any
  907. ) -> Var[VAR_TYPE | OTHER_VAR_TYPE]:
  908. """Perform a logical AND operation on the current instance and another variable.
  909. Args:
  910. other: The variable to perform the logical AND operation with.
  911. Returns:
  912. A `BooleanVar` object representing the result of the logical AND operation.
  913. """
  914. return and_operation(self, other)
  915. def __rand__(
  916. self, other: Var[OTHER_VAR_TYPE] | Any
  917. ) -> Var[VAR_TYPE | OTHER_VAR_TYPE]:
  918. """Perform a logical AND operation on the current instance and another variable.
  919. Args:
  920. other: The variable to perform the logical AND operation with.
  921. Returns:
  922. A `BooleanVar` object representing the result of the logical AND operation.
  923. """
  924. return and_operation(other, self)
  925. def __or__(
  926. self, other: Var[OTHER_VAR_TYPE] | Any
  927. ) -> Var[VAR_TYPE | OTHER_VAR_TYPE]:
  928. """Perform a logical OR operation on the current instance and another variable.
  929. Args:
  930. other: The variable to perform the logical OR operation with.
  931. Returns:
  932. A `BooleanVar` object representing the result of the logical OR operation.
  933. """
  934. return or_operation(self, other)
  935. def __ror__(
  936. self, other: Var[OTHER_VAR_TYPE] | Any
  937. ) -> Var[VAR_TYPE | OTHER_VAR_TYPE]:
  938. """Perform a logical OR operation on the current instance and another variable.
  939. Args:
  940. other: The variable to perform the logical OR operation with.
  941. Returns:
  942. A `BooleanVar` object representing the result of the logical OR operation.
  943. """
  944. return or_operation(other, self)
  945. def __invert__(self) -> BooleanVar:
  946. """Perform a logical NOT operation on the current instance.
  947. Returns:
  948. A `BooleanVar` object representing the result of the logical NOT operation.
  949. """
  950. return ~self.bool()
  951. def to_string(self, use_json: bool = True) -> StringVar:
  952. """Convert the var to a string.
  953. Args:
  954. use_json: Whether to use JSON stringify. If False, uses Object.prototype.toString.
  955. Returns:
  956. The string var.
  957. """
  958. from .function import JSON_STRINGIFY, PROTOTYPE_TO_STRING
  959. from .sequence import StringVar
  960. return (
  961. JSON_STRINGIFY.call(self).to(StringVar)
  962. if use_json
  963. else PROTOTYPE_TO_STRING.call(self).to(StringVar)
  964. )
  965. def _as_ref(self) -> Var:
  966. """Get a reference to the var.
  967. Returns:
  968. The reference to the var.
  969. """
  970. from .object import ObjectVar
  971. refs = Var(
  972. _js_expr="refs",
  973. _var_data=VarData(
  974. imports={
  975. f"$/{constants.Dirs.STATE_PATH}": [imports.ImportVar(tag="refs")]
  976. }
  977. ),
  978. ).to(ObjectVar, Mapping[str, str])
  979. return refs[LiteralVar.create(str(self))]
  980. @deprecated("Use `.js_type()` instead.")
  981. def _type(self) -> StringVar:
  982. """Returns the type of the object.
  983. This method uses the `typeof` function from the `FunctionStringVar` class
  984. to determine the type of the object.
  985. Returns:
  986. StringVar: A string variable representing the type of the object.
  987. """
  988. return self.js_type()
  989. def js_type(self) -> StringVar:
  990. """Returns the javascript type of the object.
  991. This method uses the `typeof` function from the `FunctionStringVar` class
  992. to determine the type of the object.
  993. Returns:
  994. StringVar: A string variable representing the type of the object.
  995. """
  996. from .function import FunctionStringVar
  997. from .sequence import StringVar
  998. type_of = FunctionStringVar("typeof")
  999. return type_of.call(self).to(StringVar)
  1000. def _without_data(self):
  1001. """Create a copy of the var without the data.
  1002. Returns:
  1003. The var without the data.
  1004. """
  1005. return dataclasses.replace(self, _var_data=None)
  1006. def __get__(self, instance: Any, owner: Any):
  1007. """Get the var.
  1008. Args:
  1009. instance: The instance to get the var from.
  1010. owner: The owner of the var.
  1011. Returns:
  1012. The var.
  1013. """
  1014. return self
  1015. def _decode(self) -> Any:
  1016. """Decode Var as a python value.
  1017. Note that Var with state set cannot be decoded python-side and will be
  1018. returned as full_name.
  1019. Returns:
  1020. The decoded value or the Var name.
  1021. """
  1022. if isinstance(self, LiteralVar):
  1023. return self._var_value
  1024. try:
  1025. return json.loads(str(self))
  1026. except ValueError:
  1027. return str(self)
  1028. @property
  1029. def _var_state(self) -> str:
  1030. """Compat method for getting the state.
  1031. Returns:
  1032. The state name associated with the var.
  1033. """
  1034. var_data = self._get_all_var_data()
  1035. return var_data.state if var_data else ""
  1036. @overload
  1037. @classmethod
  1038. def range(cls, stop: int | NumberVar, /) -> ArrayVar[Sequence[int]]: ...
  1039. @overload
  1040. @classmethod
  1041. def range(
  1042. cls,
  1043. start: int | NumberVar,
  1044. end: int | NumberVar,
  1045. step: int | NumberVar = 1,
  1046. /,
  1047. ) -> ArrayVar[Sequence[int]]: ...
  1048. @classmethod
  1049. def range(
  1050. cls,
  1051. first_endpoint: int | NumberVar,
  1052. second_endpoint: int | NumberVar | None = None,
  1053. step: int | NumberVar | None = None,
  1054. ) -> ArrayVar[Sequence[int]]:
  1055. """Create a range of numbers.
  1056. Args:
  1057. first_endpoint: The end of the range if second_endpoint is not provided, otherwise the start of the range.
  1058. second_endpoint: The end of the range.
  1059. step: The step of the range.
  1060. Returns:
  1061. The range of numbers.
  1062. """
  1063. from .sequence import ArrayVar
  1064. return ArrayVar.range(first_endpoint, second_endpoint, step)
  1065. if not TYPE_CHECKING:
  1066. def __getitem__(self, key: Any) -> Var:
  1067. """Get the item from the var.
  1068. Args:
  1069. key: The key to get.
  1070. Raises:
  1071. UntypedVarError: If the var type is Any.
  1072. TypeError: If the var type is Any.
  1073. # noqa: DAR101 self
  1074. """
  1075. if self._var_type is Any:
  1076. raise exceptions.UntypedVarError(
  1077. self,
  1078. f"access the item '{key}'",
  1079. )
  1080. raise TypeError(
  1081. f"Var of type {self._var_type} does not support item access."
  1082. )
  1083. def __getattr__(self, name: str):
  1084. """Get an attribute of the var.
  1085. Args:
  1086. name: The name of the attribute.
  1087. Raises:
  1088. VarAttributeError: If the attribute does not exist.
  1089. UntypedVarError: If the var type is Any.
  1090. TypeError: If the var type is Any.
  1091. # noqa: DAR101 self
  1092. """
  1093. if name.startswith("_"):
  1094. raise VarAttributeError(f"Attribute {name} not found.")
  1095. if name == "contains":
  1096. raise TypeError(
  1097. f"Var of type {self._var_type} does not support contains check."
  1098. )
  1099. if name == "reverse":
  1100. raise TypeError("Cannot reverse non-list var.")
  1101. if self._var_type is Any:
  1102. raise exceptions.UntypedVarError(
  1103. self,
  1104. f"access the attribute '{name}'",
  1105. )
  1106. raise VarAttributeError(
  1107. f"The State var {escape(self._js_expr)} of type {escape(str(self._var_type))} has no attribute '{name}' or may have been annotated wrongly.",
  1108. )
  1109. def __bool__(self) -> bool:
  1110. """Raise exception if using Var in a boolean context.
  1111. Raises:
  1112. VarTypeError: when attempting to bool-ify the Var.
  1113. # noqa: DAR101 self
  1114. """
  1115. raise VarTypeError(
  1116. f"Cannot convert Var {str(self)!r} to bool for use with `if`, `and`, `or`, and `not`. "
  1117. "Instead use `rx.cond` and bitwise operators `&` (and), `|` (or), `~` (invert)."
  1118. )
  1119. def __iter__(self) -> Any:
  1120. """Raise exception if using Var in an iterable context.
  1121. Raises:
  1122. VarTypeError: when attempting to iterate over the Var.
  1123. # noqa: DAR101 self
  1124. """
  1125. raise VarTypeError(
  1126. f"Cannot iterate over Var {str(self)!r}. Instead use `rx.foreach`."
  1127. )
  1128. def __contains__(self, _: Any) -> Var:
  1129. """Override the 'in' operator to alert the user that it is not supported.
  1130. Raises:
  1131. VarTypeError: the operation is not supported
  1132. # noqa: DAR101 self
  1133. """
  1134. raise VarTypeError(
  1135. "'in' operator not supported for Var types, use Var.contains() instead."
  1136. )
  1137. OUTPUT = TypeVar("OUTPUT", bound=Var)
  1138. VAR_SUBCLASS = TypeVar("VAR_SUBCLASS", bound=Var)
  1139. VAR_INSIDE = TypeVar("VAR_INSIDE")
  1140. class ToOperation:
  1141. """A var operation that converts a var to another type."""
  1142. def __getattr__(self, name: str) -> Any:
  1143. """Get an attribute of the var.
  1144. Args:
  1145. name: The name of the attribute.
  1146. Returns:
  1147. The attribute of the var.
  1148. """
  1149. from .object import ObjectVar
  1150. if isinstance(self, ObjectVar) and name != "_js_expr":
  1151. return ObjectVar.__getattr__(self, name)
  1152. return getattr(self._original, name)
  1153. def __post_init__(self):
  1154. """Post initialization."""
  1155. object.__delattr__(self, "_js_expr")
  1156. def __hash__(self) -> int:
  1157. """Calculate the hash value of the object.
  1158. Returns:
  1159. int: The hash value of the object.
  1160. """
  1161. return hash(self._original)
  1162. def _get_all_var_data(self) -> VarData | None:
  1163. """Get all the var data.
  1164. Returns:
  1165. The var data.
  1166. """
  1167. return VarData.merge(
  1168. self._original._get_all_var_data(),
  1169. self._var_data,
  1170. )
  1171. @classmethod
  1172. def create(
  1173. cls,
  1174. value: Var,
  1175. _var_type: GenericType | None = None,
  1176. _var_data: VarData | None = None,
  1177. ):
  1178. """Create a ToOperation.
  1179. Args:
  1180. value: The value of the var.
  1181. _var_type: The type of the Var.
  1182. _var_data: Additional hooks and imports associated with the Var.
  1183. Returns:
  1184. The ToOperation.
  1185. """
  1186. return cls(
  1187. _js_expr="", # pyright: ignore [reportCallIssue]
  1188. _var_data=_var_data, # pyright: ignore [reportCallIssue]
  1189. _var_type=_var_type or cls._default_var_type, # pyright: ignore [reportCallIssue, reportAttributeAccessIssue]
  1190. _original=value, # pyright: ignore [reportCallIssue]
  1191. )
  1192. class LiteralVar(Var):
  1193. """Base class for immutable literal vars."""
  1194. def __init_subclass__(cls, **kwargs):
  1195. """Initialize the subclass.
  1196. Args:
  1197. **kwargs: Additional keyword arguments.
  1198. Raises:
  1199. TypeError: If the LiteralVar subclass does not have a corresponding Var subclass.
  1200. """
  1201. super().__init_subclass__(**kwargs)
  1202. bases = cls.__bases__
  1203. bases_normalized = [
  1204. base if inspect.isclass(base) else get_origin(base) for base in bases
  1205. ]
  1206. possible_bases = [
  1207. base
  1208. for base in bases_normalized
  1209. if safe_issubclass(base, Var) and base != LiteralVar
  1210. ]
  1211. if not possible_bases:
  1212. raise TypeError(
  1213. f"LiteralVar subclass {cls} must have a base class that is a subclass of Var and not LiteralVar."
  1214. )
  1215. var_subclasses = [
  1216. var_subclass
  1217. for var_subclass in _var_subclasses
  1218. if var_subclass.var_subclass in possible_bases
  1219. ]
  1220. if not var_subclasses:
  1221. raise TypeError(
  1222. f"LiteralVar {cls} must have a base class annotated with `python_types`."
  1223. )
  1224. if len(var_subclasses) != 1:
  1225. raise TypeError(
  1226. f"LiteralVar {cls} must have exactly one base class annotated with `python_types`."
  1227. )
  1228. var_subclass = var_subclasses[0]
  1229. # Remove the old subclass, happens because __init_subclass__ is called twice
  1230. # for each subclass. This is because of __slots__ in dataclasses.
  1231. for var_literal_subclass in list(_var_literal_subclasses):
  1232. if var_literal_subclass[1] is var_subclass:
  1233. _var_literal_subclasses.remove(var_literal_subclass)
  1234. _var_literal_subclasses.append((cls, var_subclass))
  1235. @classmethod
  1236. def _create_literal_var(
  1237. cls,
  1238. value: Any,
  1239. _var_data: VarData | None = None,
  1240. ) -> Var:
  1241. """Create a var from a value.
  1242. Args:
  1243. value: The value to create the var from.
  1244. _var_data: Additional hooks and imports associated with the Var.
  1245. Returns:
  1246. The var.
  1247. Raises:
  1248. TypeError: If the value is not a supported type for LiteralVar.
  1249. """
  1250. from .object import LiteralObjectVar
  1251. from .sequence import ArrayVar, LiteralStringVar
  1252. if isinstance(value, Var):
  1253. if _var_data is None:
  1254. return value
  1255. return value._replace(merge_var_data=_var_data)
  1256. for literal_subclass, var_subclass in _var_literal_subclasses[::-1]:
  1257. if isinstance(value, var_subclass.python_types):
  1258. return literal_subclass.create(value, _var_data=_var_data)
  1259. from reflex.event import EventHandler
  1260. from reflex.utils.format import get_event_handler_parts
  1261. if isinstance(value, EventHandler):
  1262. return Var(_js_expr=".".join(filter(None, get_event_handler_parts(value))))
  1263. serialized_value = serializers.serialize(value)
  1264. if serialized_value is not None:
  1265. if isinstance(serialized_value, Mapping):
  1266. return LiteralObjectVar.create(
  1267. serialized_value,
  1268. _var_type=type(value),
  1269. _var_data=_var_data,
  1270. )
  1271. if isinstance(serialized_value, str):
  1272. return LiteralStringVar.create(
  1273. serialized_value, _var_type=type(value), _var_data=_var_data
  1274. )
  1275. return LiteralVar.create(serialized_value, _var_data=_var_data)
  1276. if isinstance(value, Base):
  1277. # get the fields of the pydantic class
  1278. fields = value.__fields__.keys()
  1279. one_level_dict = {field: getattr(value, field) for field in fields}
  1280. return LiteralObjectVar.create(
  1281. {
  1282. field: value
  1283. for field, value in one_level_dict.items()
  1284. if not callable(value)
  1285. },
  1286. _var_type=type(value),
  1287. _var_data=_var_data,
  1288. )
  1289. if dataclasses.is_dataclass(value) and not isinstance(value, type):
  1290. return LiteralObjectVar.create(
  1291. {
  1292. k: (None if callable(v) else v)
  1293. for k, v in dataclasses.asdict(value).items()
  1294. },
  1295. _var_type=type(value),
  1296. _var_data=_var_data,
  1297. )
  1298. if isinstance(value, range):
  1299. return ArrayVar.range(value.start, value.stop, value.step)
  1300. raise TypeError(
  1301. f"Unsupported type {type(value)} for LiteralVar. Tried to create a LiteralVar from {value}."
  1302. )
  1303. if not TYPE_CHECKING:
  1304. create = _create_literal_var
  1305. def __post_init__(self):
  1306. """Post-initialize the var."""
  1307. @property
  1308. def _var_value(self) -> Any:
  1309. raise NotImplementedError(
  1310. "LiteralVar subclasses must implement the _var_value property."
  1311. )
  1312. def json(self) -> str:
  1313. """Serialize the var to a JSON string.
  1314. Raises:
  1315. NotImplementedError: If the method is not implemented.
  1316. """
  1317. raise NotImplementedError(
  1318. "LiteralVar subclasses must implement the json method."
  1319. )
  1320. @serializers.serializer
  1321. def serialize_literal(value: LiteralVar):
  1322. """Serialize a Literal type.
  1323. Args:
  1324. value: The Literal to serialize.
  1325. Returns:
  1326. The serialized Literal.
  1327. """
  1328. return value._var_value
  1329. def get_python_literal(value: LiteralVar | Any) -> Any | None:
  1330. """Get the Python literal value.
  1331. Args:
  1332. value: The value to get the Python literal value of.
  1333. Returns:
  1334. The Python literal value.
  1335. """
  1336. if isinstance(value, LiteralVar):
  1337. return value._var_value
  1338. if isinstance(value, Var):
  1339. return None
  1340. return value
  1341. P = ParamSpec("P")
  1342. T = TypeVar("T")
  1343. # NoReturn is used to match CustomVarOperationReturn with no type hint.
  1344. @overload
  1345. def var_operation( # pyright: ignore [reportOverlappingOverload]
  1346. func: Callable[P, CustomVarOperationReturn[NoReturn]],
  1347. ) -> Callable[P, Var]: ...
  1348. @overload
  1349. def var_operation(
  1350. func: Callable[P, CustomVarOperationReturn[None]],
  1351. ) -> Callable[P, NoneVar]: ...
  1352. @overload
  1353. def var_operation( # pyright: ignore [reportOverlappingOverload]
  1354. func: Callable[P, CustomVarOperationReturn[bool]]
  1355. | Callable[P, CustomVarOperationReturn[bool | None]],
  1356. ) -> Callable[P, BooleanVar]: ...
  1357. NUMBER_T = TypeVar("NUMBER_T", int, float, int | float)
  1358. @overload
  1359. def var_operation(
  1360. func: Callable[P, CustomVarOperationReturn[NUMBER_T]]
  1361. | Callable[P, CustomVarOperationReturn[NUMBER_T | None]],
  1362. ) -> Callable[P, NumberVar[NUMBER_T]]: ...
  1363. @overload
  1364. def var_operation(
  1365. func: Callable[P, CustomVarOperationReturn[str]]
  1366. | Callable[P, CustomVarOperationReturn[str | None]],
  1367. ) -> Callable[P, StringVar]: ...
  1368. LIST_T = TypeVar("LIST_T", bound=Sequence)
  1369. @overload
  1370. def var_operation(
  1371. func: Callable[P, CustomVarOperationReturn[LIST_T]]
  1372. | Callable[P, CustomVarOperationReturn[LIST_T | None]],
  1373. ) -> Callable[P, ArrayVar[LIST_T]]: ...
  1374. OBJECT_TYPE = TypeVar("OBJECT_TYPE", bound=Mapping)
  1375. @overload
  1376. def var_operation(
  1377. func: Callable[P, CustomVarOperationReturn[OBJECT_TYPE]]
  1378. | Callable[P, CustomVarOperationReturn[OBJECT_TYPE | None]],
  1379. ) -> Callable[P, ObjectVar[OBJECT_TYPE]]: ...
  1380. @overload
  1381. def var_operation(
  1382. func: Callable[P, CustomVarOperationReturn[T]]
  1383. | Callable[P, CustomVarOperationReturn[T | None]],
  1384. ) -> Callable[P, Var[T]]: ...
  1385. def var_operation( # pyright: ignore [reportInconsistentOverload]
  1386. func: Callable[P, CustomVarOperationReturn[T]],
  1387. ) -> Callable[P, Var[T]]:
  1388. """Decorator for creating a var operation.
  1389. Example:
  1390. ```python
  1391. @var_operation
  1392. def add(a: NumberVar, b: NumberVar):
  1393. return custom_var_operation(f"{a} + {b}")
  1394. ```
  1395. Args:
  1396. func: The function to decorate.
  1397. Returns:
  1398. The decorated function.
  1399. """
  1400. @functools.wraps(func)
  1401. def wrapper(*args: P.args, **kwargs: P.kwargs) -> Var[T]:
  1402. func_args = list(inspect.signature(func).parameters)
  1403. args_vars = {
  1404. func_args[i]: (LiteralVar.create(arg) if not isinstance(arg, Var) else arg)
  1405. for i, arg in enumerate(args)
  1406. }
  1407. kwargs_vars = {
  1408. key: LiteralVar.create(value) if not isinstance(value, Var) else value
  1409. for key, value in kwargs.items()
  1410. }
  1411. return CustomVarOperation.create(
  1412. name=func.__name__,
  1413. args=tuple(list(args_vars.items()) + list(kwargs_vars.items())),
  1414. return_var=func(*args_vars.values(), **kwargs_vars), # pyright: ignore [reportCallIssue, reportReturnType]
  1415. ).guess_type()
  1416. return wrapper
  1417. def figure_out_type(value: Any) -> types.GenericType:
  1418. """Figure out the type of the value.
  1419. Args:
  1420. value: The value to figure out the type of.
  1421. Returns:
  1422. The type of the value.
  1423. """
  1424. if isinstance(value, Var):
  1425. return value._var_type
  1426. type_ = type(value)
  1427. if has_args(type_):
  1428. return type_
  1429. if isinstance(value, list):
  1430. if not value:
  1431. return Sequence[NoReturn]
  1432. return Sequence[unionize(*(figure_out_type(v) for v in value))]
  1433. if isinstance(value, set):
  1434. return set[unionize(*(figure_out_type(v) for v in value))]
  1435. if isinstance(value, tuple):
  1436. if not value:
  1437. return tuple[NoReturn, ...]
  1438. if len(value) <= 5:
  1439. return tuple[tuple(figure_out_type(v) for v in value)]
  1440. return tuple[unionize(*(figure_out_type(v) for v in value)), ...]
  1441. if isinstance(value, Mapping):
  1442. if not value:
  1443. return Mapping[NoReturn, NoReturn]
  1444. return Mapping[
  1445. unionize(*(figure_out_type(k) for k in value)),
  1446. unionize(*(figure_out_type(v) for v in value.values())),
  1447. ]
  1448. return type(value)
  1449. GLOBAL_CACHE = {}
  1450. class cached_property: # noqa: N801
  1451. """A cached property that caches the result of the function."""
  1452. def __init__(self, func: Callable):
  1453. """Initialize the cached_property.
  1454. Args:
  1455. func: The function to cache.
  1456. """
  1457. self._func = func
  1458. self._attrname = None
  1459. def __set_name__(self, owner: Any, name: str):
  1460. """Set the name of the cached property.
  1461. Args:
  1462. owner: The owner of the cached property.
  1463. name: The name of the cached property.
  1464. Raises:
  1465. TypeError: If the cached property is assigned to two different names.
  1466. """
  1467. if self._attrname is None:
  1468. self._attrname = name
  1469. original_del = getattr(owner, "__del__", None)
  1470. def delete_property(this: Any):
  1471. """Delete the cached property.
  1472. Args:
  1473. this: The object to delete the cached property from.
  1474. """
  1475. cached_field_name = "_reflex_cache_" + name
  1476. try:
  1477. unique_id = object.__getattribute__(this, cached_field_name)
  1478. except AttributeError:
  1479. if original_del is not None:
  1480. original_del(this)
  1481. return
  1482. GLOBAL_CACHE.pop(unique_id, None)
  1483. if original_del is not None:
  1484. original_del(this)
  1485. owner.__del__ = delete_property
  1486. elif name != self._attrname:
  1487. raise TypeError(
  1488. "Cannot assign the same cached_property to two different names "
  1489. f"({self._attrname!r} and {name!r})."
  1490. )
  1491. def __get__(self, instance: Any, owner: type | None = None):
  1492. """Get the cached property.
  1493. Args:
  1494. instance: The instance to get the cached property from.
  1495. owner: The owner of the cached property.
  1496. Returns:
  1497. The cached property.
  1498. Raises:
  1499. TypeError: If the class does not have __set_name__.
  1500. """
  1501. if self._attrname is None:
  1502. raise TypeError(
  1503. "Cannot use cached_property on a class without __set_name__."
  1504. )
  1505. cached_field_name = "_reflex_cache_" + self._attrname
  1506. try:
  1507. unique_id = object.__getattribute__(instance, cached_field_name)
  1508. except AttributeError:
  1509. unique_id = uuid.uuid4().int
  1510. object.__setattr__(instance, cached_field_name, unique_id)
  1511. if unique_id not in GLOBAL_CACHE:
  1512. GLOBAL_CACHE[unique_id] = self._func(instance)
  1513. return GLOBAL_CACHE[unique_id]
  1514. cached_property_no_lock = cached_property
  1515. class VarProtocol(Protocol):
  1516. """A protocol for Var."""
  1517. __dataclass_fields__: ClassVar[dict[str, dataclasses.Field[Any]]]
  1518. @property
  1519. def _js_expr(self) -> str: ...
  1520. @property
  1521. def _var_type(self) -> types.GenericType: ...
  1522. @property
  1523. def _var_data(self) -> VarData: ...
  1524. class CachedVarOperation:
  1525. """Base class for cached var operations to lower boilerplate code."""
  1526. def __post_init__(self):
  1527. """Post-initialize the CachedVarOperation."""
  1528. object.__delattr__(self, "_js_expr")
  1529. def __getattr__(self, name: str) -> Any:
  1530. """Get an attribute of the var.
  1531. Args:
  1532. name: The name of the attribute.
  1533. Returns:
  1534. The attribute.
  1535. """
  1536. if name == "_js_expr":
  1537. return self._cached_var_name
  1538. parent_classes = inspect.getmro(type(self))
  1539. next_class = parent_classes[parent_classes.index(CachedVarOperation) + 1]
  1540. return next_class.__getattr__(self, name)
  1541. def _get_all_var_data(self) -> VarData | None:
  1542. """Get all VarData associated with the Var.
  1543. Returns:
  1544. The VarData of the components and all of its children.
  1545. """
  1546. return self._cached_get_all_var_data
  1547. @cached_property_no_lock
  1548. def _cached_get_all_var_data(self: VarProtocol) -> VarData | None:
  1549. """Get the cached VarData.
  1550. Returns:
  1551. The cached VarData.
  1552. """
  1553. return VarData.merge(
  1554. *(
  1555. value._get_all_var_data() if isinstance(value, Var) else None
  1556. for value in (
  1557. getattr(self, field.name) for field in dataclasses.fields(self)
  1558. )
  1559. ),
  1560. self._var_data,
  1561. )
  1562. def __hash__(self: DataclassInstance) -> int:
  1563. """Calculate the hash of the object.
  1564. Returns:
  1565. The hash of the object.
  1566. """
  1567. return hash(
  1568. (
  1569. type(self).__name__,
  1570. *[
  1571. getattr(self, field.name)
  1572. for field in dataclasses.fields(self)
  1573. if field.name not in ["_js_expr", "_var_data", "_var_type"]
  1574. ],
  1575. )
  1576. )
  1577. def and_operation(
  1578. a: Var[VAR_TYPE] | Any, b: Var[OTHER_VAR_TYPE] | Any
  1579. ) -> Var[VAR_TYPE | OTHER_VAR_TYPE]:
  1580. """Perform a logical AND operation on two variables.
  1581. Args:
  1582. a: The first variable.
  1583. b: The second variable.
  1584. Returns:
  1585. The result of the logical AND operation.
  1586. """
  1587. return _and_operation(a, b)
  1588. @var_operation
  1589. def _and_operation(a: Var, b: Var):
  1590. """Perform a logical AND operation on two variables.
  1591. Args:
  1592. a: The first variable.
  1593. b: The second variable.
  1594. Returns:
  1595. The result of the logical AND operation.
  1596. """
  1597. return var_operation_return(
  1598. js_expression=f"({a} && {b})",
  1599. var_type=unionize(a._var_type, b._var_type),
  1600. )
  1601. def or_operation(
  1602. a: Var[VAR_TYPE] | Any, b: Var[OTHER_VAR_TYPE] | Any
  1603. ) -> Var[VAR_TYPE | OTHER_VAR_TYPE]:
  1604. """Perform a logical OR operation on two variables.
  1605. Args:
  1606. a: The first variable.
  1607. b: The second variable.
  1608. Returns:
  1609. The result of the logical OR operation.
  1610. """
  1611. return _or_operation(a, b)
  1612. @var_operation
  1613. def _or_operation(a: Var, b: Var):
  1614. """Perform a logical OR operation on two variables.
  1615. Args:
  1616. a: The first variable.
  1617. b: The second variable.
  1618. Returns:
  1619. The result of the logical OR operation.
  1620. """
  1621. return var_operation_return(
  1622. js_expression=f"({a} || {b})",
  1623. var_type=unionize(a._var_type, b._var_type),
  1624. )
  1625. RETURN_TYPE = TypeVar("RETURN_TYPE")
  1626. DICT_KEY = TypeVar("DICT_KEY")
  1627. DICT_VAL = TypeVar("DICT_VAL")
  1628. LIST_INSIDE = TypeVar("LIST_INSIDE")
  1629. class FakeComputedVarBaseClass(property):
  1630. """A fake base class for ComputedVar to avoid inheriting from property."""
  1631. __pydantic_run_validation__ = False
  1632. def is_computed_var(obj: Any) -> TypeGuard[ComputedVar]:
  1633. """Check if the object is a ComputedVar.
  1634. Args:
  1635. obj: The object to check.
  1636. Returns:
  1637. Whether the object is a ComputedVar.
  1638. """
  1639. return isinstance(obj, FakeComputedVarBaseClass)
  1640. @dataclasses.dataclass(
  1641. eq=False,
  1642. frozen=True,
  1643. slots=True,
  1644. )
  1645. class ComputedVar(Var[RETURN_TYPE]):
  1646. """A field with computed getters."""
  1647. # Whether to track dependencies and cache computed values
  1648. _cache: bool = dataclasses.field(default=False)
  1649. # Whether the computed var is a backend var
  1650. _backend: bool = dataclasses.field(default=False)
  1651. # The initial value of the computed var
  1652. _initial_value: RETURN_TYPE | types.Unset = dataclasses.field(default=types.Unset())
  1653. # Explicit var dependencies to track
  1654. _static_deps: dict[str | None, set[str]] = dataclasses.field(default_factory=dict)
  1655. # Whether var dependencies should be auto-determined
  1656. _auto_deps: bool = dataclasses.field(default=True)
  1657. # Interval at which the computed var should be updated
  1658. _update_interval: datetime.timedelta | None = dataclasses.field(default=None)
  1659. _fget: Callable[[BaseState], RETURN_TYPE] = dataclasses.field(
  1660. default_factory=lambda: lambda _: None
  1661. ) # pyright: ignore [reportAssignmentType]
  1662. def __init__(
  1663. self,
  1664. fget: Callable[[BASE_STATE], RETURN_TYPE],
  1665. initial_value: RETURN_TYPE | types.Unset = types.Unset(),
  1666. cache: bool = True,
  1667. deps: list[str | Var] | None = None,
  1668. auto_deps: bool = True,
  1669. interval: int | datetime.timedelta | None = None,
  1670. backend: bool | None = None,
  1671. **kwargs,
  1672. ):
  1673. """Initialize a ComputedVar.
  1674. Args:
  1675. fget: The getter function.
  1676. initial_value: The initial value of the computed var.
  1677. cache: Whether to cache the computed value.
  1678. deps: Explicit var dependencies to track.
  1679. auto_deps: Whether var dependencies should be auto-determined.
  1680. interval: Interval at which the computed var should be updated.
  1681. backend: Whether the computed var is a backend var.
  1682. **kwargs: additional attributes to set on the instance
  1683. Raises:
  1684. TypeError: If the computed var dependencies are not Var instances or var names.
  1685. UntypedComputedVarError: If the computed var is untyped.
  1686. """
  1687. hint = kwargs.pop("return_type", None) or get_type_hints(fget).get(
  1688. "return", Any
  1689. )
  1690. if hint is Any:
  1691. raise UntypedComputedVarError(var_name=fget.__name__)
  1692. kwargs.setdefault("_js_expr", fget.__name__)
  1693. kwargs.setdefault("_var_type", hint)
  1694. Var.__init__(
  1695. self,
  1696. _js_expr=kwargs.pop("_js_expr"),
  1697. _var_type=kwargs.pop("_var_type"),
  1698. _var_data=kwargs.pop("_var_data", None),
  1699. )
  1700. if kwargs:
  1701. raise TypeError(f"Unexpected keyword arguments: {tuple(kwargs)}")
  1702. if backend is None:
  1703. backend = fget.__name__.startswith("_")
  1704. object.__setattr__(self, "_backend", backend)
  1705. object.__setattr__(self, "_initial_value", initial_value)
  1706. object.__setattr__(self, "_cache", cache)
  1707. if isinstance(interval, int):
  1708. interval = datetime.timedelta(seconds=interval)
  1709. object.__setattr__(self, "_update_interval", interval)
  1710. object.__setattr__(
  1711. self,
  1712. "_static_deps",
  1713. self._calculate_static_deps(deps),
  1714. )
  1715. object.__setattr__(self, "_auto_deps", auto_deps)
  1716. object.__setattr__(self, "_fget", fget)
  1717. def _calculate_static_deps(
  1718. self,
  1719. deps: list[str | Var] | dict[str | None, set[str]] | None = None,
  1720. ) -> dict[str | None, set[str]]:
  1721. """Calculate the static dependencies of the computed var from user input or existing dependencies.
  1722. Args:
  1723. deps: The user input dependencies or existing dependencies.
  1724. Returns:
  1725. The static dependencies.
  1726. """
  1727. if isinstance(deps, dict):
  1728. # Assume a dict is coming from _replace, so no special processing.
  1729. return deps
  1730. _static_deps = {}
  1731. if deps is not None:
  1732. for dep in deps:
  1733. _static_deps = self._add_static_dep(dep, _static_deps)
  1734. return _static_deps
  1735. def _add_static_dep(
  1736. self, dep: str | Var, deps: dict[str | None, set[str]] | None = None
  1737. ) -> dict[str | None, set[str]]:
  1738. """Add a static dependency to the computed var or existing dependency set.
  1739. Args:
  1740. dep: The dependency to add.
  1741. deps: The existing dependency set.
  1742. Returns:
  1743. The updated dependency set.
  1744. Raises:
  1745. TypeError: If the computed var dependencies are not Var instances or var names.
  1746. """
  1747. if deps is None:
  1748. deps = self._static_deps
  1749. if isinstance(dep, Var):
  1750. state_name = (
  1751. all_var_data.state
  1752. if (all_var_data := dep._get_all_var_data()) and all_var_data.state
  1753. else None
  1754. )
  1755. if all_var_data is not None:
  1756. var_name = all_var_data.field_name
  1757. else:
  1758. var_name = dep._js_expr
  1759. deps.setdefault(state_name, set()).add(var_name)
  1760. elif isinstance(dep, str) and dep != "":
  1761. deps.setdefault(None, set()).add(dep)
  1762. else:
  1763. raise TypeError(
  1764. "ComputedVar dependencies must be Var instances or var names (non-empty strings)."
  1765. )
  1766. return deps
  1767. @override
  1768. def _replace(
  1769. self,
  1770. merge_var_data: VarData | None = None,
  1771. **kwargs: Any,
  1772. ) -> Self:
  1773. """Replace the attributes of the ComputedVar.
  1774. Args:
  1775. merge_var_data: VarData to merge into the existing VarData.
  1776. **kwargs: Var fields to update.
  1777. Returns:
  1778. The new ComputedVar instance.
  1779. Raises:
  1780. TypeError: If kwargs contains keys that are not allowed.
  1781. """
  1782. if "deps" in kwargs:
  1783. kwargs["deps"] = self._calculate_static_deps(kwargs["deps"])
  1784. field_values = {
  1785. "fget": kwargs.pop("fget", self._fget),
  1786. "initial_value": kwargs.pop("initial_value", self._initial_value),
  1787. "cache": kwargs.pop("cache", self._cache),
  1788. "deps": kwargs.pop("deps", copy.copy(self._static_deps)),
  1789. "auto_deps": kwargs.pop("auto_deps", self._auto_deps),
  1790. "interval": kwargs.pop("interval", self._update_interval),
  1791. "backend": kwargs.pop("backend", self._backend),
  1792. "_js_expr": kwargs.pop("_js_expr", self._js_expr),
  1793. "_var_type": kwargs.pop("_var_type", self._var_type),
  1794. "_var_data": kwargs.pop(
  1795. "_var_data", VarData.merge(self._var_data, merge_var_data)
  1796. ),
  1797. "return_type": kwargs.pop("return_type", self._var_type),
  1798. }
  1799. if kwargs:
  1800. unexpected_kwargs = ", ".join(kwargs.keys())
  1801. raise TypeError(f"Unexpected keyword arguments: {unexpected_kwargs}")
  1802. return type(self)(**field_values)
  1803. @property
  1804. def _cache_attr(self) -> str:
  1805. """Get the attribute used to cache the value on the instance.
  1806. Returns:
  1807. An attribute name.
  1808. """
  1809. return f"__cached_{self._js_expr}"
  1810. @property
  1811. def _last_updated_attr(self) -> str:
  1812. """Get the attribute used to store the last updated timestamp.
  1813. Returns:
  1814. An attribute name.
  1815. """
  1816. return f"__last_updated_{self._js_expr}"
  1817. def needs_update(self, instance: BaseState) -> bool:
  1818. """Check if the computed var needs to be updated.
  1819. Args:
  1820. instance: The state instance that the computed var is attached to.
  1821. Returns:
  1822. True if the computed var needs to be updated, False otherwise.
  1823. """
  1824. if self._update_interval is None:
  1825. return False
  1826. last_updated = getattr(instance, self._last_updated_attr, None)
  1827. if last_updated is None:
  1828. return True
  1829. return datetime.datetime.now() - last_updated > self._update_interval
  1830. @overload
  1831. def __get__(
  1832. self: ComputedVar[bool],
  1833. instance: None,
  1834. owner: type,
  1835. ) -> BooleanVar: ...
  1836. @overload
  1837. def __get__(
  1838. self: ComputedVar[int] | ComputedVar[float],
  1839. instance: None,
  1840. owner: type,
  1841. ) -> NumberVar: ...
  1842. @overload
  1843. def __get__(
  1844. self: ComputedVar[str],
  1845. instance: None,
  1846. owner: type,
  1847. ) -> StringVar: ...
  1848. @overload
  1849. def __get__(
  1850. self: ComputedVar[MAPPING_TYPE],
  1851. instance: None,
  1852. owner: type,
  1853. ) -> ObjectVar[MAPPING_TYPE]: ...
  1854. @overload
  1855. def __get__(
  1856. self: ComputedVar[list[LIST_INSIDE]],
  1857. instance: None,
  1858. owner: type,
  1859. ) -> ArrayVar[list[LIST_INSIDE]]: ...
  1860. @overload
  1861. def __get__(
  1862. self: ComputedVar[tuple[LIST_INSIDE, ...]],
  1863. instance: None,
  1864. owner: type,
  1865. ) -> ArrayVar[tuple[LIST_INSIDE, ...]]: ...
  1866. @overload
  1867. def __get__(
  1868. self: ComputedVar[BASE_TYPE],
  1869. instance: None,
  1870. owner: type,
  1871. ) -> ObjectVar[BASE_TYPE]: ...
  1872. @overload
  1873. def __get__(
  1874. self: ComputedVar[SQLA_TYPE],
  1875. instance: None,
  1876. owner: type,
  1877. ) -> ObjectVar[SQLA_TYPE]: ...
  1878. if TYPE_CHECKING:
  1879. @overload
  1880. def __get__(
  1881. self: ComputedVar[DATACLASS_TYPE], instance: None, owner: Any
  1882. ) -> ObjectVar[DATACLASS_TYPE]: ...
  1883. @overload
  1884. def __get__(self, instance: None, owner: type) -> ComputedVar[RETURN_TYPE]: ...
  1885. @overload
  1886. def __get__(self, instance: BaseState, owner: type) -> RETURN_TYPE: ...
  1887. def __get__(self, instance: BaseState | None, owner: type):
  1888. """Get the ComputedVar value.
  1889. If the value is already cached on the instance, return the cached value.
  1890. Args:
  1891. instance: the instance of the class accessing this computed var.
  1892. owner: the class that this descriptor is attached to.
  1893. Returns:
  1894. The value of the var for the given instance.
  1895. """
  1896. if instance is None:
  1897. state_where_defined = owner
  1898. while self._js_expr in state_where_defined.inherited_vars:
  1899. state_where_defined = state_where_defined.get_parent_state()
  1900. field_name = (
  1901. format_state_name(state_where_defined.get_full_name())
  1902. + "."
  1903. + self._js_expr
  1904. )
  1905. return dispatch(
  1906. field_name,
  1907. var_data=VarData.from_state(state_where_defined, self._js_expr),
  1908. result_var_type=self._var_type,
  1909. existing_var=self,
  1910. )
  1911. if not self._cache:
  1912. value = self.fget(instance)
  1913. else:
  1914. # handle caching
  1915. if not hasattr(instance, self._cache_attr) or self.needs_update(instance):
  1916. # Set cache attr on state instance.
  1917. setattr(instance, self._cache_attr, self.fget(instance))
  1918. # Ensure the computed var gets serialized to redis.
  1919. instance._was_touched = True
  1920. # Set the last updated timestamp on the state instance.
  1921. setattr(instance, self._last_updated_attr, datetime.datetime.now())
  1922. value = getattr(instance, self._cache_attr)
  1923. self._check_deprecated_return_type(instance, value)
  1924. return value
  1925. def _check_deprecated_return_type(self, instance: BaseState, value: Any) -> None:
  1926. if not _isinstance(value, self._var_type, nested=1, treat_var_as_type=False):
  1927. console.error(
  1928. f"Computed var '{type(instance).__name__}.{self._js_expr}' must return"
  1929. f" a value of type '{escape(str(self._var_type))}', got '{value!s}' of type {type(value)}."
  1930. )
  1931. def _deps(
  1932. self,
  1933. objclass: type[BaseState],
  1934. obj: FunctionType | CodeType | None = None,
  1935. ) -> dict[str, set[str]]:
  1936. """Determine var dependencies of this ComputedVar.
  1937. Save references to attributes accessed on "self" or other fetched states.
  1938. Recursively called when the function makes a method call on "self" or
  1939. define comprehensions or nested functions that may reference "self".
  1940. Args:
  1941. objclass: the class obj this ComputedVar is attached to.
  1942. obj: the object to disassemble (defaults to the fget function).
  1943. Returns:
  1944. A dictionary mapping state names to the set of variable names
  1945. accessed by the given obj.
  1946. """
  1947. from .dep_tracking import DependencyTracker
  1948. d = {}
  1949. if self._static_deps:
  1950. d.update(self._static_deps)
  1951. # None is a placeholder for the current state class.
  1952. if None in d:
  1953. d[objclass.get_full_name()] = d.pop(None)
  1954. if not self._auto_deps:
  1955. return d
  1956. if obj is None:
  1957. fget = self._fget
  1958. if fget is not None:
  1959. obj = cast(FunctionType, fget)
  1960. else:
  1961. return d
  1962. try:
  1963. return DependencyTracker(
  1964. func=obj, state_cls=objclass, dependencies=d
  1965. ).dependencies
  1966. except Exception as e:
  1967. console.warn(
  1968. "Failed to automatically determine dependencies for computed var "
  1969. f"{objclass.__name__}.{self._js_expr}: {e}. "
  1970. "Provide static_deps and set auto_deps=False to suppress this warning."
  1971. )
  1972. return d
  1973. def mark_dirty(self, instance: BaseState) -> None:
  1974. """Mark this ComputedVar as dirty.
  1975. Args:
  1976. instance: the state instance that needs to recompute the value.
  1977. """
  1978. with contextlib.suppress(AttributeError):
  1979. delattr(instance, self._cache_attr)
  1980. def add_dependency(self, objclass: type[BaseState], dep: Var):
  1981. """Explicitly add a dependency to the ComputedVar.
  1982. After adding the dependency, when the `dep` changes, this computed var
  1983. will be marked dirty.
  1984. Args:
  1985. objclass: The class obj this ComputedVar is attached to.
  1986. dep: The dependency to add.
  1987. Raises:
  1988. VarDependencyError: If the dependency is not a Var instance with a
  1989. state and field name
  1990. """
  1991. if all_var_data := dep._get_all_var_data():
  1992. state_name = all_var_data.state
  1993. if state_name:
  1994. var_name = all_var_data.field_name
  1995. if var_name:
  1996. self._static_deps.setdefault(state_name, set()).add(var_name)
  1997. objclass.get_root_state().get_class_substate(
  1998. state_name
  1999. )._var_dependencies.setdefault(var_name, set()).add(
  2000. (objclass.get_full_name(), self._js_expr)
  2001. )
  2002. return
  2003. raise VarDependencyError(
  2004. "ComputedVar dependencies must be Var instances with a state and "
  2005. f"field name, got {dep!r}."
  2006. )
  2007. def _determine_var_type(self) -> type:
  2008. """Get the type of the var.
  2009. Returns:
  2010. The type of the var.
  2011. """
  2012. hints = get_type_hints(self._fget)
  2013. if "return" in hints:
  2014. return hints["return"]
  2015. return Any # pyright: ignore [reportReturnType]
  2016. @property
  2017. def __class__(self) -> type:
  2018. """Get the class of the var.
  2019. Returns:
  2020. The class of the var.
  2021. """
  2022. return FakeComputedVarBaseClass
  2023. @property
  2024. def fget(self) -> Callable[[BaseState], RETURN_TYPE]:
  2025. """Get the getter function.
  2026. Returns:
  2027. The getter function.
  2028. """
  2029. return self._fget
  2030. class DynamicRouteVar(ComputedVar[str | list[str]]):
  2031. """A ComputedVar that represents a dynamic route."""
  2032. pass
  2033. async def _default_async_computed_var(_self: BaseState) -> Any:
  2034. return None
  2035. @dataclasses.dataclass(
  2036. eq=False,
  2037. frozen=True,
  2038. init=False,
  2039. slots=True,
  2040. )
  2041. class AsyncComputedVar(ComputedVar[RETURN_TYPE]):
  2042. """A computed var that wraps a coroutinefunction."""
  2043. _fget: Callable[[BaseState], Coroutine[None, None, RETURN_TYPE]] = (
  2044. dataclasses.field(default=_default_async_computed_var)
  2045. )
  2046. @overload
  2047. def __get__(
  2048. self: AsyncComputedVar[bool],
  2049. instance: None,
  2050. owner: type,
  2051. ) -> BooleanVar: ...
  2052. @overload
  2053. def __get__(
  2054. self: AsyncComputedVar[int] | ComputedVar[float],
  2055. instance: None,
  2056. owner: type,
  2057. ) -> NumberVar: ...
  2058. @overload
  2059. def __get__(
  2060. self: AsyncComputedVar[str],
  2061. instance: None,
  2062. owner: type,
  2063. ) -> StringVar: ...
  2064. @overload
  2065. def __get__(
  2066. self: AsyncComputedVar[MAPPING_TYPE],
  2067. instance: None,
  2068. owner: type,
  2069. ) -> ObjectVar[MAPPING_TYPE]: ...
  2070. @overload
  2071. def __get__(
  2072. self: AsyncComputedVar[list[LIST_INSIDE]],
  2073. instance: None,
  2074. owner: type,
  2075. ) -> ArrayVar[list[LIST_INSIDE]]: ...
  2076. @overload
  2077. def __get__(
  2078. self: AsyncComputedVar[tuple[LIST_INSIDE, ...]],
  2079. instance: None,
  2080. owner: type,
  2081. ) -> ArrayVar[tuple[LIST_INSIDE, ...]]: ...
  2082. @overload
  2083. def __get__(
  2084. self: AsyncComputedVar[BASE_TYPE],
  2085. instance: None,
  2086. owner: type,
  2087. ) -> ObjectVar[BASE_TYPE]: ...
  2088. @overload
  2089. def __get__(
  2090. self: AsyncComputedVar[SQLA_TYPE],
  2091. instance: None,
  2092. owner: type,
  2093. ) -> ObjectVar[SQLA_TYPE]: ...
  2094. if TYPE_CHECKING:
  2095. @overload
  2096. def __get__(
  2097. self: AsyncComputedVar[DATACLASS_TYPE], instance: None, owner: Any
  2098. ) -> ObjectVar[DATACLASS_TYPE]: ...
  2099. @overload
  2100. def __get__(self, instance: None, owner: type) -> AsyncComputedVar[RETURN_TYPE]: ...
  2101. @overload
  2102. def __get__(
  2103. self, instance: BaseState, owner: type
  2104. ) -> Coroutine[None, None, RETURN_TYPE]: ...
  2105. def __get__(
  2106. self, instance: BaseState | None, owner
  2107. ) -> Var | Coroutine[None, None, RETURN_TYPE]:
  2108. """Get the ComputedVar value.
  2109. If the value is already cached on the instance, return the cached value.
  2110. Args:
  2111. instance: the instance of the class accessing this computed var.
  2112. owner: the class that this descriptor is attached to.
  2113. Returns:
  2114. The value of the var for the given instance.
  2115. """
  2116. if instance is None:
  2117. return super(AsyncComputedVar, self).__get__(instance, owner)
  2118. if not self._cache:
  2119. async def _awaitable_result(instance: BaseState = instance) -> RETURN_TYPE:
  2120. value = await self.fget(instance)
  2121. self._check_deprecated_return_type(instance, value)
  2122. return value
  2123. return _awaitable_result()
  2124. else:
  2125. # handle caching
  2126. async def _awaitable_result(instance: BaseState = instance) -> RETURN_TYPE:
  2127. if not hasattr(instance, self._cache_attr) or self.needs_update(
  2128. instance
  2129. ):
  2130. # Set cache attr on state instance.
  2131. setattr(instance, self._cache_attr, await self.fget(instance))
  2132. # Ensure the computed var gets serialized to redis.
  2133. instance._was_touched = True
  2134. # Set the last updated timestamp on the state instance.
  2135. setattr(instance, self._last_updated_attr, datetime.datetime.now())
  2136. value = getattr(instance, self._cache_attr)
  2137. self._check_deprecated_return_type(instance, value)
  2138. return value
  2139. return _awaitable_result()
  2140. @property
  2141. def fget(self) -> Callable[[BaseState], Coroutine[None, None, RETURN_TYPE]]:
  2142. """Get the getter function.
  2143. Returns:
  2144. The getter function.
  2145. """
  2146. return self._fget
  2147. if TYPE_CHECKING:
  2148. BASE_STATE = TypeVar("BASE_STATE", bound=BaseState)
  2149. class _ComputedVarDecorator(Protocol):
  2150. """A protocol for the ComputedVar decorator."""
  2151. @overload
  2152. def __call__(
  2153. self,
  2154. fget: Callable[[BASE_STATE], Coroutine[Any, Any, RETURN_TYPE]],
  2155. ) -> AsyncComputedVar[RETURN_TYPE]: ...
  2156. @overload
  2157. def __call__(
  2158. self,
  2159. fget: Callable[[BASE_STATE], RETURN_TYPE],
  2160. ) -> ComputedVar[RETURN_TYPE]: ...
  2161. def __call__(
  2162. self,
  2163. fget: Callable[[BASE_STATE], Any],
  2164. ) -> ComputedVar[Any]: ...
  2165. @overload
  2166. def computed_var(
  2167. fget: None = None,
  2168. initial_value: Any | types.Unset = types.Unset(),
  2169. cache: bool = True,
  2170. deps: list[str | Var] | None = None,
  2171. auto_deps: bool = True,
  2172. interval: datetime.timedelta | int | None = None,
  2173. backend: bool | None = None,
  2174. **kwargs,
  2175. ) -> _ComputedVarDecorator: ...
  2176. @overload
  2177. def computed_var(
  2178. fget: Callable[[BASE_STATE], Coroutine[Any, Any, RETURN_TYPE]],
  2179. initial_value: RETURN_TYPE | types.Unset = types.Unset(),
  2180. cache: bool = True,
  2181. deps: list[str | Var] | None = None,
  2182. auto_deps: bool = True,
  2183. interval: datetime.timedelta | int | None = None,
  2184. backend: bool | None = None,
  2185. **kwargs,
  2186. ) -> AsyncComputedVar[RETURN_TYPE]: ...
  2187. @overload
  2188. def computed_var(
  2189. fget: Callable[[BASE_STATE], RETURN_TYPE],
  2190. initial_value: RETURN_TYPE | types.Unset = types.Unset(),
  2191. cache: bool = True,
  2192. deps: list[str | Var] | None = None,
  2193. auto_deps: bool = True,
  2194. interval: datetime.timedelta | int | None = None,
  2195. backend: bool | None = None,
  2196. **kwargs,
  2197. ) -> ComputedVar[RETURN_TYPE]: ...
  2198. def computed_var(
  2199. fget: Callable[[BASE_STATE], Any] | None = None,
  2200. initial_value: Any | types.Unset = types.Unset(),
  2201. cache: bool = True,
  2202. deps: list[str | Var] | None = None,
  2203. auto_deps: bool = True,
  2204. interval: datetime.timedelta | int | None = None,
  2205. backend: bool | None = None,
  2206. **kwargs,
  2207. ) -> ComputedVar | Callable[[Callable[[BASE_STATE], Any]], ComputedVar]:
  2208. """A ComputedVar decorator with or without kwargs.
  2209. Args:
  2210. fget: The getter function.
  2211. initial_value: The initial value of the computed var.
  2212. cache: Whether to cache the computed value.
  2213. deps: Explicit var dependencies to track.
  2214. auto_deps: Whether var dependencies should be auto-determined.
  2215. interval: Interval at which the computed var should be updated.
  2216. backend: Whether the computed var is a backend var.
  2217. **kwargs: additional attributes to set on the instance
  2218. Returns:
  2219. A ComputedVar instance.
  2220. Raises:
  2221. ValueError: If caching is disabled and an update interval is set.
  2222. VarDependencyError: If user supplies dependencies without caching.
  2223. ComputedVarSignatureError: If the getter function has more than one argument.
  2224. """
  2225. if cache is False and interval is not None:
  2226. raise ValueError("Cannot set update interval without caching.")
  2227. if cache is False and (deps is not None or auto_deps is False):
  2228. raise VarDependencyError("Cannot track dependencies without caching.")
  2229. if fget is not None:
  2230. sign = inspect.signature(fget)
  2231. if len(sign.parameters) != 1:
  2232. raise ComputedVarSignatureError(fget.__name__, signature=str(sign))
  2233. if inspect.iscoroutinefunction(fget):
  2234. computed_var_cls = AsyncComputedVar
  2235. else:
  2236. computed_var_cls = ComputedVar
  2237. return computed_var_cls(
  2238. fget,
  2239. initial_value=initial_value,
  2240. cache=cache,
  2241. deps=deps,
  2242. auto_deps=auto_deps,
  2243. interval=interval,
  2244. backend=backend,
  2245. **kwargs,
  2246. )
  2247. def wrapper(fget: Callable[[BASE_STATE], Any]) -> ComputedVar:
  2248. if inspect.iscoroutinefunction(fget):
  2249. computed_var_cls = AsyncComputedVar
  2250. else:
  2251. computed_var_cls = ComputedVar
  2252. return computed_var_cls(
  2253. fget,
  2254. initial_value=initial_value,
  2255. cache=cache,
  2256. deps=deps,
  2257. auto_deps=auto_deps,
  2258. interval=interval,
  2259. backend=backend,
  2260. **kwargs,
  2261. )
  2262. return wrapper
  2263. RETURN = TypeVar("RETURN")
  2264. class CustomVarOperationReturn(Var[RETURN]):
  2265. """Base class for custom var operations."""
  2266. @classmethod
  2267. def create(
  2268. cls,
  2269. js_expression: str,
  2270. _var_type: type[RETURN] | None = None,
  2271. _var_data: VarData | None = None,
  2272. ) -> CustomVarOperationReturn[RETURN]:
  2273. """Create a CustomVarOperation.
  2274. Args:
  2275. js_expression: The JavaScript expression to evaluate.
  2276. _var_type: The type of the var.
  2277. _var_data: Additional hooks and imports associated with the Var.
  2278. Returns:
  2279. The CustomVarOperation.
  2280. """
  2281. return CustomVarOperationReturn(
  2282. _js_expr=js_expression,
  2283. _var_type=_var_type or Any,
  2284. _var_data=_var_data,
  2285. )
  2286. def var_operation_return(
  2287. js_expression: str,
  2288. var_type: type[RETURN] | GenericType | None = None,
  2289. var_data: VarData | None = None,
  2290. ) -> CustomVarOperationReturn[RETURN]:
  2291. """Shortcut for creating a CustomVarOperationReturn.
  2292. Args:
  2293. js_expression: The JavaScript expression to evaluate.
  2294. var_type: The type of the var.
  2295. var_data: Additional hooks and imports associated with the Var.
  2296. Returns:
  2297. The CustomVarOperationReturn.
  2298. """
  2299. return CustomVarOperationReturn.create(
  2300. js_expression,
  2301. var_type,
  2302. var_data,
  2303. )
  2304. @dataclasses.dataclass(
  2305. eq=False,
  2306. frozen=True,
  2307. slots=True,
  2308. )
  2309. class CustomVarOperation(CachedVarOperation, Var[T]):
  2310. """Base class for custom var operations."""
  2311. _name: str = dataclasses.field(default="")
  2312. _args: tuple[tuple[str, Var], ...] = dataclasses.field(default_factory=tuple)
  2313. _return: CustomVarOperationReturn[T] = dataclasses.field(
  2314. default_factory=lambda: CustomVarOperationReturn.create("")
  2315. )
  2316. @cached_property_no_lock
  2317. def _cached_var_name(self) -> str:
  2318. """Get the cached var name.
  2319. Returns:
  2320. The cached var name.
  2321. """
  2322. return str(self._return)
  2323. @cached_property_no_lock
  2324. def _cached_get_all_var_data(self) -> VarData | None:
  2325. """Get the cached VarData.
  2326. Returns:
  2327. The cached VarData.
  2328. """
  2329. return VarData.merge(
  2330. *(arg[1]._get_all_var_data() for arg in self._args),
  2331. self._return._get_all_var_data(),
  2332. self._var_data,
  2333. )
  2334. @classmethod
  2335. def create(
  2336. cls,
  2337. name: str,
  2338. args: tuple[tuple[str, Var], ...],
  2339. return_var: CustomVarOperationReturn[T],
  2340. _var_data: VarData | None = None,
  2341. ) -> CustomVarOperation[T]:
  2342. """Create a CustomVarOperation.
  2343. Args:
  2344. name: The name of the operation.
  2345. args: The arguments to the operation.
  2346. return_var: The return var.
  2347. _var_data: Additional hooks and imports associated with the Var.
  2348. Returns:
  2349. The CustomVarOperation.
  2350. """
  2351. return CustomVarOperation(
  2352. _js_expr="",
  2353. _var_type=return_var._var_type,
  2354. _var_data=_var_data,
  2355. _name=name,
  2356. _args=args,
  2357. _return=return_var,
  2358. )
  2359. class NoneVar(Var[None], python_types=type(None)):
  2360. """A var representing None."""
  2361. @dataclasses.dataclass(
  2362. eq=False,
  2363. frozen=True,
  2364. slots=True,
  2365. )
  2366. class LiteralNoneVar(LiteralVar, NoneVar):
  2367. """A var representing None."""
  2368. _var_value: None = None
  2369. def json(self) -> str:
  2370. """Serialize the var to a JSON string.
  2371. Returns:
  2372. The JSON string.
  2373. """
  2374. return "null"
  2375. @classmethod
  2376. def create(
  2377. cls,
  2378. value: None = None,
  2379. _var_data: VarData | None = None,
  2380. ) -> LiteralNoneVar:
  2381. """Create a var from a value.
  2382. Args:
  2383. value: The value of the var. Must be None. Existed for compatibility with LiteralVar.
  2384. _var_data: Additional hooks and imports associated with the Var.
  2385. Returns:
  2386. The var.
  2387. """
  2388. return LiteralNoneVar(
  2389. _js_expr="null",
  2390. _var_type=None,
  2391. _var_data=_var_data,
  2392. )
  2393. def get_to_operation(var_subclass: type[Var]) -> type[ToOperation]:
  2394. """Get the ToOperation class for a given Var subclass.
  2395. Args:
  2396. var_subclass: The Var subclass.
  2397. Returns:
  2398. The ToOperation class.
  2399. Raises:
  2400. ValueError: If the ToOperation class cannot be found.
  2401. """
  2402. possible_classes = [
  2403. saved_var_subclass.to_var_subclass
  2404. for saved_var_subclass in _var_subclasses
  2405. if saved_var_subclass.var_subclass is var_subclass
  2406. ]
  2407. if not possible_classes:
  2408. raise ValueError(f"Could not find ToOperation for {var_subclass}.")
  2409. return possible_classes[0]
  2410. @dataclasses.dataclass(
  2411. eq=False,
  2412. frozen=True,
  2413. slots=True,
  2414. )
  2415. class StateOperation(CachedVarOperation, Var):
  2416. """A var operation that accesses a field on an object."""
  2417. _state_name: str = dataclasses.field(default="")
  2418. _field: Var = dataclasses.field(default_factory=lambda: LiteralNoneVar.create())
  2419. @cached_property_no_lock
  2420. def _cached_var_name(self) -> str:
  2421. """Get the cached var name.
  2422. Returns:
  2423. The cached var name.
  2424. """
  2425. return f"{self._state_name!s}.{self._field!s}"
  2426. def __getattr__(self, name: str) -> Any:
  2427. """Get an attribute of the var.
  2428. Args:
  2429. name: The name of the attribute.
  2430. Returns:
  2431. The attribute.
  2432. """
  2433. if name == "_js_expr":
  2434. return self._cached_var_name
  2435. return getattr(self._field, name)
  2436. @classmethod
  2437. def create(
  2438. cls,
  2439. state_name: str,
  2440. field: Var,
  2441. _var_data: VarData | None = None,
  2442. ) -> StateOperation:
  2443. """Create a DotOperation.
  2444. Args:
  2445. state_name: The name of the state.
  2446. field: The field of the state.
  2447. _var_data: Additional hooks and imports associated with the Var.
  2448. Returns:
  2449. The DotOperation.
  2450. """
  2451. return StateOperation(
  2452. _js_expr="",
  2453. _var_type=field._var_type,
  2454. _var_data=_var_data,
  2455. _state_name=state_name,
  2456. _field=field,
  2457. )
  2458. def get_uuid_string_var() -> Var:
  2459. """Return a Var that generates a single memoized UUID via .web/utils/state.js.
  2460. useMemo with an empty dependency array ensures that the generated UUID is
  2461. consistent across re-renders of the component.
  2462. Returns:
  2463. A Var that generates a UUID at runtime.
  2464. """
  2465. from reflex.utils.imports import ImportVar
  2466. from reflex.vars import Var
  2467. unique_uuid_var = get_unique_variable_name()
  2468. unique_uuid_var_data = VarData(
  2469. imports={
  2470. f"$/{constants.Dirs.STATE_PATH}": ImportVar(tag="generateUUID"),
  2471. "react": "useMemo",
  2472. },
  2473. hooks={f"const {unique_uuid_var} = useMemo(generateUUID, [])": None},
  2474. )
  2475. return Var(
  2476. _js_expr=unique_uuid_var,
  2477. _var_type=str,
  2478. _var_data=unique_uuid_var_data,
  2479. )
  2480. # Set of unique variable names.
  2481. USED_VARIABLES = set()
  2482. def get_unique_variable_name() -> str:
  2483. """Get a unique variable name.
  2484. Returns:
  2485. The unique variable name.
  2486. """
  2487. name = "".join([random.choice(string.ascii_lowercase) for _ in range(8)])
  2488. if name not in USED_VARIABLES:
  2489. USED_VARIABLES.add(name)
  2490. return name
  2491. return get_unique_variable_name()
  2492. # Compile regex for finding reflex var tags.
  2493. _decode_var_pattern_re = (
  2494. rf"{constants.REFLEX_VAR_OPENING_TAG}(.*?){constants.REFLEX_VAR_CLOSING_TAG}"
  2495. )
  2496. _decode_var_pattern = re.compile(_decode_var_pattern_re, flags=re.DOTALL)
  2497. # Defined global immutable vars.
  2498. _global_vars: dict[int, Var] = {}
  2499. dispatchers: dict[GenericType, Callable[[Var], Var]] = {}
  2500. def transform(fn: Callable[[Var], Var]) -> Callable[[Var], Var]:
  2501. """Register a function to transform a Var.
  2502. Args:
  2503. fn: The function to register.
  2504. Returns:
  2505. The decorator.
  2506. Raises:
  2507. TypeError: If the return type of the function is not a Var.
  2508. TypeError: If the Var return type does not have a generic type.
  2509. ValueError: If a function for the generic type is already registered.
  2510. """
  2511. return_type = fn.__annotations__["return"]
  2512. origin = get_origin(return_type)
  2513. if origin is not Var:
  2514. raise TypeError(
  2515. f"Expected return type of {fn.__name__} to be a Var, got {origin}."
  2516. )
  2517. generic_args = get_args(return_type)
  2518. if not generic_args:
  2519. raise TypeError(
  2520. f"Expected Var return type of {fn.__name__} to have a generic type."
  2521. )
  2522. generic_type = get_origin(generic_args[0]) or generic_args[0]
  2523. if generic_type in dispatchers:
  2524. raise ValueError(f"Function for {generic_type} already registered.")
  2525. dispatchers[generic_type] = fn
  2526. return fn
  2527. def generic_type_to_actual_type_map(
  2528. generic_type: GenericType, actual_type: GenericType
  2529. ) -> dict[TypeVar, GenericType]:
  2530. """Map the generic type to the actual type.
  2531. Args:
  2532. generic_type: The generic type.
  2533. actual_type: The actual type.
  2534. Returns:
  2535. The mapping of type variables to actual types.
  2536. Raises:
  2537. TypeError: If the generic type and actual type do not match.
  2538. TypeError: If the number of generic arguments and actual arguments do not match.
  2539. """
  2540. generic_origin = get_origin(generic_type) or generic_type
  2541. actual_origin = get_origin(actual_type) or actual_type
  2542. if generic_origin is not actual_origin:
  2543. if isinstance(generic_origin, TypeVar):
  2544. return {generic_origin: actual_origin}
  2545. raise TypeError(
  2546. f"Type mismatch: expected {generic_origin}, got {actual_origin}."
  2547. )
  2548. generic_args = get_args(generic_type)
  2549. actual_args = get_args(actual_type)
  2550. if len(generic_args) != len(actual_args):
  2551. raise TypeError(
  2552. f"Number of generic arguments mismatch: expected {len(generic_args)}, got {len(actual_args)}."
  2553. )
  2554. # call recursively for nested generic types and merge the results
  2555. return {
  2556. k: v
  2557. for generic_arg, actual_arg in zip(generic_args, actual_args, strict=True)
  2558. for k, v in generic_type_to_actual_type_map(generic_arg, actual_arg).items()
  2559. }
  2560. def resolve_generic_type_with_mapping(
  2561. generic_type: GenericType, type_mapping: dict[TypeVar, GenericType]
  2562. ):
  2563. """Resolve a generic type with a type mapping.
  2564. Args:
  2565. generic_type: The generic type.
  2566. type_mapping: The type mapping.
  2567. Returns:
  2568. The resolved generic type.
  2569. """
  2570. if isinstance(generic_type, TypeVar):
  2571. return type_mapping.get(generic_type, generic_type)
  2572. generic_origin = get_origin(generic_type) or generic_type
  2573. generic_args = get_args(generic_type)
  2574. if not generic_args:
  2575. return generic_type
  2576. mapping_for_older_python = {
  2577. list: List, # noqa: UP006
  2578. set: Set, # noqa: UP006
  2579. dict: Dict, # noqa: UP006
  2580. tuple: Tuple, # noqa: UP006
  2581. frozenset: FrozenSet, # noqa: UP006
  2582. }
  2583. return mapping_for_older_python.get(generic_origin, generic_origin)[
  2584. tuple(
  2585. resolve_generic_type_with_mapping(arg, type_mapping) for arg in generic_args
  2586. )
  2587. ]
  2588. def resolve_arg_type_from_return_type(
  2589. arg_type: GenericType, return_type: GenericType, actual_return_type: GenericType
  2590. ) -> GenericType:
  2591. """Resolve the argument type from the return type.
  2592. Args:
  2593. arg_type: The argument type.
  2594. return_type: The return type.
  2595. actual_return_type: The requested return type.
  2596. Returns:
  2597. The argument type without the generics that are resolved.
  2598. """
  2599. return resolve_generic_type_with_mapping(
  2600. arg_type, generic_type_to_actual_type_map(return_type, actual_return_type)
  2601. )
  2602. def dispatch(
  2603. field_name: str,
  2604. var_data: VarData,
  2605. result_var_type: GenericType,
  2606. existing_var: Var | None = None,
  2607. ) -> Var:
  2608. """Dispatch a Var to the appropriate transformation function.
  2609. Args:
  2610. field_name: The name of the field.
  2611. var_data: The VarData associated with the Var.
  2612. result_var_type: The type of the Var.
  2613. existing_var: The existing Var to transform. Optional.
  2614. Returns:
  2615. The transformed Var.
  2616. Raises:
  2617. TypeError: If the return type of the function is not a Var.
  2618. TypeError: If the Var return type does not have a generic type.
  2619. TypeError: If the first argument of the function is not a Var.
  2620. TypeError: If the first argument of the function does not have a generic type
  2621. """
  2622. result_origin_var_type = get_origin(result_var_type) or result_var_type
  2623. if result_origin_var_type in dispatchers:
  2624. fn = dispatchers[result_origin_var_type]
  2625. fn_first_arg_type = next(
  2626. iter(inspect.signature(fn).parameters.values())
  2627. ).annotation
  2628. fn_return = inspect.signature(fn).return_annotation
  2629. fn_return_origin = get_origin(fn_return) or fn_return
  2630. if fn_return_origin is not Var:
  2631. raise TypeError(
  2632. f"Expected return type of {fn.__name__} to be a Var, got {fn_return}."
  2633. )
  2634. fn_return_generic_args = get_args(fn_return)
  2635. if not fn_return_generic_args:
  2636. raise TypeError(f"Expected generic type of {fn_return} to be a type.")
  2637. arg_origin = get_origin(fn_first_arg_type) or fn_first_arg_type
  2638. if arg_origin is not Var:
  2639. raise TypeError(
  2640. f"Expected first argument of {fn.__name__} to be a Var, got {fn_first_arg_type}."
  2641. )
  2642. arg_generic_args = get_args(fn_first_arg_type)
  2643. if not arg_generic_args:
  2644. raise TypeError(
  2645. f"Expected generic type of {fn_first_arg_type} to be a type."
  2646. )
  2647. arg_type = arg_generic_args[0]
  2648. fn_return_type = fn_return_generic_args[0]
  2649. var = (
  2650. Var(
  2651. field_name,
  2652. _var_data=var_data,
  2653. _var_type=resolve_arg_type_from_return_type(
  2654. arg_type, fn_return_type, result_var_type
  2655. ),
  2656. ).guess_type()
  2657. if existing_var is None
  2658. else existing_var._replace(
  2659. _var_type=resolve_arg_type_from_return_type(
  2660. arg_type, fn_return_type, result_var_type
  2661. ),
  2662. _var_data=var_data,
  2663. _js_expr=field_name,
  2664. ).guess_type()
  2665. )
  2666. return fn(var)
  2667. if existing_var is not None:
  2668. return existing_var._replace(
  2669. _js_expr=field_name,
  2670. _var_data=var_data,
  2671. _var_type=result_var_type,
  2672. ).guess_type()
  2673. return Var(
  2674. field_name,
  2675. _var_data=var_data,
  2676. _var_type=result_var_type,
  2677. ).guess_type()
  2678. if TYPE_CHECKING:
  2679. from _typeshed import DataclassInstance
  2680. from sqlalchemy.orm import DeclarativeBase
  2681. SQLA_TYPE = TypeVar("SQLA_TYPE", bound=DeclarativeBase | None)
  2682. BASE_TYPE = TypeVar("BASE_TYPE", bound=Base | None)
  2683. DATACLASS_TYPE = TypeVar("DATACLASS_TYPE", bound=DataclassInstance | None)
  2684. MAPPING_TYPE = TypeVar("MAPPING_TYPE", bound=Mapping | None)
  2685. V = TypeVar("V")
  2686. FIELD_TYPE = TypeVar("FIELD_TYPE")
  2687. class Field(Generic[FIELD_TYPE]):
  2688. """Shadow class for Var to allow for type hinting in the IDE."""
  2689. def __set__(self, instance: Any, value: FIELD_TYPE):
  2690. """Set the Var.
  2691. Args:
  2692. instance: The instance of the class setting the Var.
  2693. value: The value to set the Var to.
  2694. """
  2695. @overload
  2696. def __get__(self: Field[None], instance: None, owner: Any) -> NoneVar: ...
  2697. @overload
  2698. def __get__(
  2699. self: Field[bool] | Field[bool | None], instance: None, owner: Any
  2700. ) -> BooleanVar: ...
  2701. @overload
  2702. def __get__(
  2703. self: Field[int] | Field[int | None],
  2704. instance: None,
  2705. owner: Any,
  2706. ) -> NumberVar[int]: ...
  2707. @overload
  2708. def __get__(
  2709. self: Field[float]
  2710. | Field[int | float]
  2711. | Field[float | None]
  2712. | Field[int | float | None],
  2713. instance: None,
  2714. owner: Any,
  2715. ) -> NumberVar: ...
  2716. @overload
  2717. def __get__(
  2718. self: Field[str] | Field[str | None], instance: None, owner: Any
  2719. ) -> StringVar: ...
  2720. @overload
  2721. def __get__(
  2722. self: Field[list[V]]
  2723. | Field[set[V]]
  2724. | Field[list[V] | None]
  2725. | Field[set[V] | None],
  2726. instance: None,
  2727. owner: Any,
  2728. ) -> ArrayVar[Sequence[V]]: ...
  2729. @overload
  2730. def __get__(
  2731. self: Field[SEQUENCE_TYPE] | Field[SEQUENCE_TYPE | None],
  2732. instance: None,
  2733. owner: Any,
  2734. ) -> ArrayVar[SEQUENCE_TYPE]: ...
  2735. @overload
  2736. def __get__(
  2737. self: Field[MAPPING_TYPE] | Field[MAPPING_TYPE | None],
  2738. instance: None,
  2739. owner: Any,
  2740. ) -> ObjectVar[MAPPING_TYPE]: ...
  2741. @overload
  2742. def __get__(
  2743. self: Field[BASE_TYPE] | Field[BASE_TYPE | None], instance: None, owner: Any
  2744. ) -> ObjectVar[BASE_TYPE]: ...
  2745. @overload
  2746. def __get__(
  2747. self: Field[SQLA_TYPE] | Field[SQLA_TYPE | None], instance: None, owner: Any
  2748. ) -> ObjectVar[SQLA_TYPE]: ...
  2749. if TYPE_CHECKING:
  2750. @overload
  2751. def __get__(
  2752. self: Field[DATACLASS_TYPE] | Field[DATACLASS_TYPE | None],
  2753. instance: None,
  2754. owner: Any,
  2755. ) -> ObjectVar[DATACLASS_TYPE]: ...
  2756. @overload
  2757. def __get__(self, instance: None, owner: Any) -> Var[FIELD_TYPE]: ...
  2758. @overload
  2759. def __get__(self, instance: Any, owner: Any) -> FIELD_TYPE: ...
  2760. def __get__(self, instance: Any, owner: Any): # pyright: ignore [reportInconsistentOverload]
  2761. """Get the Var.
  2762. Args:
  2763. instance: The instance of the class accessing the Var.
  2764. owner: The class that the Var is attached to.
  2765. """
  2766. def field(value: FIELD_TYPE) -> Field[FIELD_TYPE]:
  2767. """Create a Field with a value.
  2768. Args:
  2769. value: The value of the Field.
  2770. Returns:
  2771. The Field.
  2772. """
  2773. return value # pyright: ignore [reportReturnType]