event.py 60 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077
  1. """Define event classes to connect the frontend and backend."""
  2. from __future__ import annotations
  3. import dataclasses
  4. import inspect
  5. import sys
  6. import types
  7. import urllib.parse
  8. from base64 import b64encode
  9. from functools import partial
  10. from typing import (
  11. TYPE_CHECKING,
  12. Any,
  13. Callable,
  14. Dict,
  15. Generic,
  16. List,
  17. Optional,
  18. Sequence,
  19. Tuple,
  20. Type,
  21. Union,
  22. get_type_hints,
  23. overload,
  24. )
  25. from typing_extensions import (
  26. Concatenate,
  27. ParamSpec,
  28. Protocol,
  29. TypeAliasType,
  30. TypedDict,
  31. TypeVar,
  32. TypeVarTuple,
  33. Unpack,
  34. deprecated,
  35. get_args,
  36. get_origin,
  37. )
  38. from reflex import constants
  39. from reflex.constants.state import FRONTEND_EVENT_STATE
  40. from reflex.utils import console, format
  41. from reflex.utils.exceptions import EventFnArgMismatch, EventHandlerArgTypeMismatch
  42. from reflex.utils.types import ArgsSpec, GenericType, typehint_issubclass
  43. from reflex.vars import VarData
  44. from reflex.vars.base import LiteralVar, Var
  45. from reflex.vars.function import (
  46. ArgsFunctionOperation,
  47. ArgsFunctionOperationBuilder,
  48. BuilderFunctionVar,
  49. FunctionArgs,
  50. FunctionStringVar,
  51. FunctionVar,
  52. VarOperationCall,
  53. )
  54. from reflex.vars.object import ObjectVar
  55. try:
  56. from typing import Annotated
  57. except ImportError:
  58. from typing_extensions import Annotated
  59. @dataclasses.dataclass(
  60. init=True,
  61. frozen=True,
  62. )
  63. class Event:
  64. """An event that describes any state change in the app."""
  65. # The token to specify the client that the event is for.
  66. token: str
  67. # The event name.
  68. name: str
  69. # The routing data where event occurred
  70. router_data: Dict[str, Any] = dataclasses.field(default_factory=dict)
  71. # The event payload.
  72. payload: Dict[str, Any] = dataclasses.field(default_factory=dict)
  73. @property
  74. def substate_token(self) -> str:
  75. """Get the substate token for the event.
  76. Returns:
  77. The substate token.
  78. """
  79. substate = self.name.rpartition(".")[0]
  80. return f"{self.token}_{substate}"
  81. _EVENT_FIELDS: set[str] = {f.name for f in dataclasses.fields(Event)}
  82. BACKGROUND_TASK_MARKER = "_reflex_background_task"
  83. def background(fn, *, __internal_reflex_call: bool = False):
  84. """Decorator to mark event handler as running in the background.
  85. Args:
  86. fn: The function to decorate.
  87. Returns:
  88. The same function, but with a marker set.
  89. Raises:
  90. TypeError: If the function is not a coroutine function or async generator.
  91. """
  92. if not __internal_reflex_call:
  93. console.deprecate(
  94. "background-decorator",
  95. "Use `rx.event(background=True)` instead.",
  96. "0.6.5",
  97. "0.7.0",
  98. )
  99. if not inspect.iscoroutinefunction(fn) and not inspect.isasyncgenfunction(fn):
  100. raise TypeError("Background task must be async function or generator.")
  101. setattr(fn, BACKGROUND_TASK_MARKER, True)
  102. return fn
  103. @dataclasses.dataclass(
  104. init=True,
  105. frozen=True,
  106. )
  107. class EventActionsMixin:
  108. """Mixin for DOM event actions."""
  109. # Whether to `preventDefault` or `stopPropagation` on the event.
  110. event_actions: Dict[str, Union[bool, int]] = dataclasses.field(default_factory=dict)
  111. @property
  112. def stop_propagation(self):
  113. """Stop the event from bubbling up the DOM tree.
  114. Returns:
  115. New EventHandler-like with stopPropagation set to True.
  116. """
  117. return dataclasses.replace(
  118. self,
  119. event_actions={"stopPropagation": True, **self.event_actions},
  120. )
  121. @property
  122. def prevent_default(self):
  123. """Prevent the default behavior of the event.
  124. Returns:
  125. New EventHandler-like with preventDefault set to True.
  126. """
  127. return dataclasses.replace(
  128. self,
  129. event_actions={"preventDefault": True, **self.event_actions},
  130. )
  131. def throttle(self, limit_ms: int):
  132. """Throttle the event handler.
  133. Args:
  134. limit_ms: The time in milliseconds to throttle the event handler.
  135. Returns:
  136. New EventHandler-like with throttle set to limit_ms.
  137. """
  138. return dataclasses.replace(
  139. self,
  140. event_actions={"throttle": limit_ms, **self.event_actions},
  141. )
  142. def debounce(self, delay_ms: int):
  143. """Debounce the event handler.
  144. Args:
  145. delay_ms: The time in milliseconds to debounce the event handler.
  146. Returns:
  147. New EventHandler-like with debounce set to delay_ms.
  148. """
  149. return dataclasses.replace(
  150. self,
  151. event_actions={"debounce": delay_ms, **self.event_actions},
  152. )
  153. @property
  154. def temporal(self):
  155. """Do not queue the event if the backend is down.
  156. Returns:
  157. New EventHandler-like with temporal set to True.
  158. """
  159. return dataclasses.replace(
  160. self,
  161. event_actions={"temporal": True, **self.event_actions},
  162. )
  163. @dataclasses.dataclass(
  164. init=True,
  165. frozen=True,
  166. )
  167. class EventHandler(EventActionsMixin):
  168. """An event handler responds to an event to update the state."""
  169. # The function to call in response to the event.
  170. fn: Any = dataclasses.field(default=None)
  171. # The full name of the state class this event handler is attached to.
  172. # Empty string means this event handler is a server side event.
  173. state_full_name: str = dataclasses.field(default="")
  174. @classmethod
  175. def __class_getitem__(cls, args_spec: str) -> Annotated:
  176. """Get a typed EventHandler.
  177. Args:
  178. args_spec: The args_spec of the EventHandler.
  179. Returns:
  180. The EventHandler class item.
  181. """
  182. return Annotated[cls, args_spec]
  183. @property
  184. def is_background(self) -> bool:
  185. """Whether the event handler is a background task.
  186. Returns:
  187. True if the event handler is marked as a background task.
  188. """
  189. return getattr(self.fn, BACKGROUND_TASK_MARKER, False)
  190. def __call__(self, *args: Any) -> EventSpec:
  191. """Pass arguments to the handler to get an event spec.
  192. This method configures event handlers that take in arguments.
  193. Args:
  194. *args: The arguments to pass to the handler.
  195. Returns:
  196. The event spec, containing both the function and args.
  197. Raises:
  198. EventHandlerTypeError: If the arguments are invalid.
  199. """
  200. from reflex.utils.exceptions import EventHandlerTypeError
  201. # Get the function args.
  202. fn_args = inspect.getfullargspec(self.fn).args[1:]
  203. fn_args = (Var(_js_expr=arg) for arg in fn_args)
  204. # Construct the payload.
  205. values = []
  206. for arg in args:
  207. # Special case for file uploads.
  208. if isinstance(arg, FileUpload):
  209. return arg.as_event_spec(handler=self)
  210. # Otherwise, convert to JSON.
  211. try:
  212. values.append(LiteralVar.create(arg))
  213. except TypeError as e:
  214. raise EventHandlerTypeError(
  215. f"Arguments to event handlers must be Vars or JSON-serializable. Got {arg} of type {type(arg)}."
  216. ) from e
  217. payload = tuple(zip(fn_args, values))
  218. # Return the event spec.
  219. return EventSpec(
  220. handler=self, args=payload, event_actions=self.event_actions.copy()
  221. )
  222. @dataclasses.dataclass(
  223. init=True,
  224. frozen=True,
  225. )
  226. class EventSpec(EventActionsMixin):
  227. """An event specification.
  228. Whereas an Event object is passed during runtime, a spec is used
  229. during compile time to outline the structure of an event.
  230. """
  231. # The event handler.
  232. handler: EventHandler = dataclasses.field(default=None) # type: ignore
  233. # The handler on the client to process event.
  234. client_handler_name: str = dataclasses.field(default="")
  235. # The arguments to pass to the function.
  236. args: Tuple[Tuple[Var, Var], ...] = dataclasses.field(default_factory=tuple)
  237. def __init__(
  238. self,
  239. handler: EventHandler,
  240. event_actions: Dict[str, Union[bool, int]] | None = None,
  241. client_handler_name: str = "",
  242. args: Tuple[Tuple[Var, Var], ...] = (),
  243. ):
  244. """Initialize an EventSpec.
  245. Args:
  246. event_actions: The event actions.
  247. handler: The event handler.
  248. client_handler_name: The client handler name.
  249. args: The arguments to pass to the function.
  250. """
  251. if event_actions is None:
  252. event_actions = {}
  253. object.__setattr__(self, "event_actions", event_actions)
  254. object.__setattr__(self, "handler", handler)
  255. object.__setattr__(self, "client_handler_name", client_handler_name)
  256. object.__setattr__(self, "args", args or ())
  257. def with_args(self, args: Tuple[Tuple[Var, Var], ...]) -> EventSpec:
  258. """Copy the event spec, with updated args.
  259. Args:
  260. args: The new args to pass to the function.
  261. Returns:
  262. A copy of the event spec, with the new args.
  263. """
  264. return type(self)(
  265. handler=self.handler,
  266. client_handler_name=self.client_handler_name,
  267. args=args,
  268. event_actions=self.event_actions.copy(),
  269. )
  270. def add_args(self, *args: Var) -> EventSpec:
  271. """Add arguments to the event spec.
  272. Args:
  273. *args: The arguments to add positionally.
  274. Returns:
  275. The event spec with the new arguments.
  276. Raises:
  277. EventHandlerTypeError: If the arguments are invalid.
  278. """
  279. from reflex.utils.exceptions import EventHandlerTypeError
  280. # Get the remaining unfilled function args.
  281. fn_args = inspect.getfullargspec(self.handler.fn).args[1 + len(self.args) :]
  282. fn_args = (Var(_js_expr=arg) for arg in fn_args)
  283. # Construct the payload.
  284. values = []
  285. arg = None
  286. try:
  287. for arg in args:
  288. values.append(LiteralVar.create(value=arg)) # noqa: PERF401
  289. except TypeError as e:
  290. raise EventHandlerTypeError(
  291. f"Arguments to event handlers must be Vars or JSON-serializable. Got {arg} of type {type(arg)}."
  292. ) from e
  293. new_payload = tuple(zip(fn_args, values))
  294. return self.with_args(self.args + new_payload)
  295. @dataclasses.dataclass(
  296. frozen=True,
  297. )
  298. class CallableEventSpec(EventSpec):
  299. """Decorate an EventSpec-returning function to act as both a EventSpec and a function.
  300. This is used as a compatibility shim for replacing EventSpec objects in the
  301. API with functions that return a family of EventSpec.
  302. """
  303. fn: Optional[Callable[..., EventSpec]] = None
  304. def __init__(self, fn: Callable[..., EventSpec] | None = None, **kwargs):
  305. """Initialize a CallableEventSpec.
  306. Args:
  307. fn: The function to decorate.
  308. **kwargs: The kwargs to pass to pydantic initializer
  309. """
  310. if fn is not None:
  311. default_event_spec = fn()
  312. super().__init__(
  313. event_actions=default_event_spec.event_actions,
  314. client_handler_name=default_event_spec.client_handler_name,
  315. args=default_event_spec.args,
  316. handler=default_event_spec.handler,
  317. **kwargs,
  318. )
  319. object.__setattr__(self, "fn", fn)
  320. else:
  321. super().__init__(**kwargs)
  322. def __call__(self, *args, **kwargs) -> EventSpec:
  323. """Call the decorated function.
  324. Args:
  325. *args: The args to pass to the function.
  326. **kwargs: The kwargs to pass to the function.
  327. Returns:
  328. The EventSpec returned from calling the function.
  329. Raises:
  330. EventHandlerTypeError: If the CallableEventSpec has no associated function.
  331. """
  332. from reflex.utils.exceptions import EventHandlerTypeError
  333. if self.fn is None:
  334. raise EventHandlerTypeError("CallableEventSpec has no associated function.")
  335. return self.fn(*args, **kwargs)
  336. @dataclasses.dataclass(
  337. init=True,
  338. frozen=True,
  339. )
  340. class EventChain(EventActionsMixin):
  341. """Container for a chain of events that will be executed in order."""
  342. events: Sequence[Union[EventSpec, EventVar, EventCallback]] = dataclasses.field(
  343. default_factory=list
  344. )
  345. args_spec: Optional[Union[Callable, Sequence[Callable]]] = dataclasses.field(
  346. default=None
  347. )
  348. invocation: Optional[Var] = dataclasses.field(default=None)
  349. @classmethod
  350. def create(
  351. cls,
  352. value: EventType,
  353. args_spec: ArgsSpec | Sequence[ArgsSpec],
  354. key: Optional[str] = None,
  355. **event_chain_kwargs,
  356. ) -> Union[EventChain, Var]:
  357. """Create an event chain from a variety of input types.
  358. Args:
  359. value: The value to create the event chain from.
  360. args_spec: The args_spec of the event trigger being bound.
  361. key: The key of the event trigger being bound.
  362. **event_chain_kwargs: Additional kwargs to pass to the EventChain constructor.
  363. Returns:
  364. The event chain.
  365. Raises:
  366. ValueError: If the value is not a valid event chain.
  367. """
  368. # If it's an event chain var, return it.
  369. if isinstance(value, Var):
  370. if isinstance(value, EventChainVar):
  371. return value
  372. elif isinstance(value, EventVar):
  373. value = [value]
  374. elif issubclass(value._var_type, (EventChain, EventSpec)):
  375. return cls.create(
  376. value=value.guess_type(),
  377. args_spec=args_spec,
  378. key=key,
  379. **event_chain_kwargs,
  380. )
  381. else:
  382. raise ValueError(
  383. f"Invalid event chain: {value!s} of type {value._var_type}"
  384. )
  385. elif isinstance(value, EventChain):
  386. # Trust that the caller knows what they're doing passing an EventChain directly
  387. return value
  388. # If the input is a single event handler, wrap it in a list.
  389. if isinstance(value, (EventHandler, EventSpec)):
  390. value = [value]
  391. # If the input is a list of event handlers, create an event chain.
  392. if isinstance(value, List):
  393. events: List[Union[EventSpec, EventVar]] = []
  394. for v in value:
  395. if isinstance(v, (EventHandler, EventSpec)):
  396. # Call the event handler to get the event.
  397. events.append(call_event_handler(v, args_spec, key=key))
  398. elif isinstance(v, Callable):
  399. # Call the lambda to get the event chain.
  400. result = call_event_fn(v, args_spec, key=key)
  401. if isinstance(result, Var):
  402. raise ValueError(
  403. f"Invalid event chain: {v}. Cannot use a Var-returning "
  404. "lambda inside an EventChain list."
  405. )
  406. events.extend(result)
  407. elif isinstance(v, EventVar):
  408. events.append(v)
  409. else:
  410. raise ValueError(f"Invalid event: {v}")
  411. # If the input is a callable, create an event chain.
  412. elif isinstance(value, Callable):
  413. result = call_event_fn(value, args_spec, key=key)
  414. if isinstance(result, Var):
  415. # Recursively call this function if the lambda returned an EventChain Var.
  416. return cls.create(
  417. value=result, args_spec=args_spec, key=key, **event_chain_kwargs
  418. )
  419. events = [*result]
  420. # Otherwise, raise an error.
  421. else:
  422. raise ValueError(f"Invalid event chain: {value}")
  423. # Add args to the event specs if necessary.
  424. events = [
  425. (e.with_args(get_handler_args(e)) if isinstance(e, EventSpec) else e)
  426. for e in events
  427. ]
  428. # Return the event chain.
  429. return cls(
  430. events=events,
  431. args_spec=args_spec,
  432. **event_chain_kwargs,
  433. )
  434. @dataclasses.dataclass(
  435. init=True,
  436. frozen=True,
  437. )
  438. class JavascriptHTMLInputElement:
  439. """Interface for a Javascript HTMLInputElement https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement."""
  440. value: str = ""
  441. @dataclasses.dataclass(
  442. init=True,
  443. frozen=True,
  444. )
  445. class JavascriptInputEvent:
  446. """Interface for a Javascript InputEvent https://developer.mozilla.org/en-US/docs/Web/API/InputEvent."""
  447. target: JavascriptHTMLInputElement = JavascriptHTMLInputElement() # noqa: RUF009
  448. @dataclasses.dataclass(
  449. init=True,
  450. frozen=True,
  451. )
  452. class JavasciptKeyboardEvent:
  453. """Interface for a Javascript KeyboardEvent https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent."""
  454. key: str = ""
  455. altKey: bool = False
  456. ctrlKey: bool = False
  457. metaKey: bool = False
  458. shiftKey: bool = False
  459. def input_event(e: Var[JavascriptInputEvent]) -> Tuple[Var[str]]:
  460. """Get the value from an input event.
  461. Args:
  462. e: The input event.
  463. Returns:
  464. The value from the input event.
  465. """
  466. return (e.target.value,)
  467. class KeyInputInfo(TypedDict):
  468. """Information about a key input event."""
  469. alt_key: bool
  470. ctrl_key: bool
  471. meta_key: bool
  472. shift_key: bool
  473. def key_event(e: Var[JavasciptKeyboardEvent]) -> Tuple[Var[str], Var[KeyInputInfo]]:
  474. """Get the key from a keyboard event.
  475. Args:
  476. e: The keyboard event.
  477. Returns:
  478. The key from the keyboard event.
  479. """
  480. return (
  481. e.key,
  482. Var.create(
  483. {
  484. "alt_key": e.altKey,
  485. "ctrl_key": e.ctrlKey,
  486. "meta_key": e.metaKey,
  487. "shift_key": e.shiftKey,
  488. },
  489. ),
  490. )
  491. def no_args_event_spec() -> Tuple[()]:
  492. """Empty event handler.
  493. Returns:
  494. An empty tuple.
  495. """
  496. return () # type: ignore
  497. # These chains can be used for their side effects when no other events are desired.
  498. stop_propagation = EventChain(events=[], args_spec=no_args_event_spec).stop_propagation
  499. prevent_default = EventChain(events=[], args_spec=no_args_event_spec).prevent_default
  500. EVENT_T = TypeVar("EVENT_T")
  501. EVENT_U = TypeVar("EVENT_U")
  502. Ts = TypeVarTuple("Ts")
  503. class IdentityEventReturn(Generic[Unpack[Ts]], Protocol):
  504. """Protocol for an identity event return."""
  505. def __call__(self, *values: Unpack[Ts]) -> tuple[Unpack[Ts]]:
  506. """Return the input values.
  507. Args:
  508. *values: The values to return.
  509. Returns:
  510. The input values.
  511. """
  512. return values
  513. @overload
  514. def passthrough_event_spec(
  515. event_type: Type[EVENT_T], /
  516. ) -> IdentityEventReturn[Var[EVENT_T]]: ...
  517. @overload
  518. def passthrough_event_spec(
  519. event_type_1: Type[EVENT_T], event_type2: Type[EVENT_U], /
  520. ) -> IdentityEventReturn[Var[EVENT_T], Var[EVENT_U]]: ...
  521. @overload
  522. def passthrough_event_spec(
  523. *event_types: Unpack[tuple[Type[EVENT_T]]],
  524. ) -> IdentityEventReturn[Unpack[tuple[Var[EVENT_T], ...]]]: ...
  525. def passthrough_event_spec( # pyright: ignore[reportInconsistentOverload]
  526. *event_types: Type[EVENT_T],
  527. ) -> IdentityEventReturn[Unpack[tuple[Var[EVENT_T], ...]]]:
  528. """A helper function that returns the input event as output.
  529. Args:
  530. *event_types: The types of the events.
  531. Returns:
  532. A function that returns the input event as output.
  533. """
  534. def inner(*values: Var[EVENT_T]) -> Tuple[Var[EVENT_T], ...]:
  535. return values
  536. inner_type = tuple(Var[event_type] for event_type in event_types)
  537. return_annotation = Tuple[inner_type] # type: ignore
  538. inner.__signature__ = inspect.signature(inner).replace( # type: ignore
  539. parameters=[
  540. inspect.Parameter(
  541. f"ev_{i}",
  542. kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
  543. annotation=Var[event_type],
  544. )
  545. for i, event_type in enumerate(event_types)
  546. ],
  547. return_annotation=return_annotation,
  548. )
  549. for i, event_type in enumerate(event_types):
  550. inner.__annotations__[f"ev_{i}"] = Var[event_type]
  551. inner.__annotations__["return"] = return_annotation
  552. return inner
  553. @dataclasses.dataclass(
  554. init=True,
  555. frozen=True,
  556. )
  557. class FileUpload:
  558. """Class to represent a file upload."""
  559. upload_id: Optional[str] = None
  560. on_upload_progress: Optional[Union[EventHandler, Callable]] = None
  561. @staticmethod
  562. def on_upload_progress_args_spec(_prog: Var[Dict[str, Union[int, float, bool]]]):
  563. """Args spec for on_upload_progress event handler.
  564. Returns:
  565. The arg mapping passed to backend event handler
  566. """
  567. return [_prog]
  568. def as_event_spec(self, handler: EventHandler) -> EventSpec:
  569. """Get the EventSpec for the file upload.
  570. Args:
  571. handler: The event handler.
  572. Returns:
  573. The event spec for the handler.
  574. Raises:
  575. ValueError: If the on_upload_progress is not a valid event handler.
  576. """
  577. from reflex.components.core.upload import (
  578. DEFAULT_UPLOAD_ID,
  579. upload_files_context_var_data,
  580. )
  581. upload_id = self.upload_id or DEFAULT_UPLOAD_ID
  582. spec_args = [
  583. (
  584. Var(_js_expr="files"),
  585. Var(
  586. _js_expr="filesById",
  587. _var_type=Dict[str, Any],
  588. _var_data=VarData.merge(upload_files_context_var_data),
  589. ).to(ObjectVar)[LiteralVar.create(upload_id)],
  590. ),
  591. (
  592. Var(_js_expr="upload_id"),
  593. LiteralVar.create(upload_id),
  594. ),
  595. ]
  596. if self.on_upload_progress is not None:
  597. on_upload_progress = self.on_upload_progress
  598. if isinstance(on_upload_progress, EventHandler):
  599. events = [
  600. call_event_handler(
  601. on_upload_progress,
  602. self.on_upload_progress_args_spec,
  603. ),
  604. ]
  605. elif isinstance(on_upload_progress, Callable):
  606. # Call the lambda to get the event chain.
  607. events = call_event_fn(
  608. on_upload_progress, self.on_upload_progress_args_spec
  609. ) # type: ignore
  610. else:
  611. raise ValueError(f"{on_upload_progress} is not a valid event handler.")
  612. if isinstance(events, Var):
  613. raise ValueError(f"{on_upload_progress} cannot return a var {events}.")
  614. on_upload_progress_chain = EventChain(
  615. events=[*events],
  616. args_spec=self.on_upload_progress_args_spec,
  617. )
  618. formatted_chain = str(format.format_prop(on_upload_progress_chain))
  619. spec_args.append(
  620. (
  621. Var(_js_expr="on_upload_progress"),
  622. FunctionStringVar(
  623. formatted_chain.strip("{}"),
  624. ).to(FunctionVar, EventChain),
  625. ),
  626. )
  627. return EventSpec(
  628. handler=handler,
  629. client_handler_name="uploadFiles",
  630. args=tuple(spec_args),
  631. event_actions=handler.event_actions.copy(),
  632. )
  633. # Alias for rx.upload_files
  634. upload_files = FileUpload
  635. # Special server-side events.
  636. def server_side(name: str, sig: inspect.Signature, **kwargs) -> EventSpec:
  637. """A server-side event.
  638. Args:
  639. name: The name of the event.
  640. sig: The function signature of the event.
  641. **kwargs: The arguments to pass to the event.
  642. Returns:
  643. An event spec for a server-side event.
  644. """
  645. def fn():
  646. return None
  647. fn.__qualname__ = name
  648. fn.__signature__ = sig # pyright: ignore[reportFunctionMemberAccess]
  649. return EventSpec(
  650. handler=EventHandler(fn=fn, state_full_name=FRONTEND_EVENT_STATE),
  651. args=tuple(
  652. (
  653. Var(_js_expr=k),
  654. LiteralVar.create(v),
  655. )
  656. for k, v in kwargs.items()
  657. ),
  658. )
  659. @overload
  660. def redirect(
  661. path: str | Var[str],
  662. is_external: Optional[bool] = None,
  663. replace: bool = False,
  664. ) -> EventSpec: ...
  665. @overload
  666. @deprecated("`external` is deprecated use `is_external` instead")
  667. def redirect(
  668. path: str | Var[str],
  669. is_external: Optional[bool] = None,
  670. replace: bool = False,
  671. external: Optional[bool] = None,
  672. ) -> EventSpec: ...
  673. def redirect(
  674. path: str | Var[str],
  675. is_external: Optional[bool] = None,
  676. replace: bool = False,
  677. external: Optional[bool] = None,
  678. ) -> EventSpec:
  679. """Redirect to a new path.
  680. Args:
  681. path: The path to redirect to.
  682. is_external: Whether to open in new tab or not.
  683. replace: If True, the current page will not create a new history entry.
  684. external(Deprecated): Whether to open in new tab or not.
  685. Returns:
  686. An event to redirect to the path.
  687. """
  688. if external is not None:
  689. console.deprecate(
  690. "The `external` prop in `rx.redirect`",
  691. "use `is_external` instead.",
  692. "0.6.6",
  693. "0.7.0",
  694. )
  695. # is_external should take precedence over external.
  696. is_external = (
  697. (False if external is None else external)
  698. if is_external is None
  699. else is_external
  700. )
  701. return server_side(
  702. "_redirect",
  703. get_fn_signature(redirect),
  704. path=path,
  705. external=is_external,
  706. replace=replace,
  707. )
  708. def console_log(message: str | Var[str]) -> EventSpec:
  709. """Do a console.log on the browser.
  710. Args:
  711. message: The message to log.
  712. Returns:
  713. An event to log the message.
  714. """
  715. return run_script(Var("console").to(dict).log.to(FunctionVar).call(message))
  716. def noop() -> EventSpec:
  717. """Do nothing.
  718. Returns:
  719. An event to do nothing.
  720. """
  721. return run_script(Var.create(None))
  722. def back() -> EventSpec:
  723. """Do a history.back on the browser.
  724. Returns:
  725. An event to go back one page.
  726. """
  727. return run_script(
  728. Var("window").to(dict).history.to(dict).back.to(FunctionVar).call()
  729. )
  730. def window_alert(message: str | Var[str]) -> EventSpec:
  731. """Create a window alert on the browser.
  732. Args:
  733. message: The message to alert.
  734. Returns:
  735. An event to alert the message.
  736. """
  737. return run_script(Var("window").to(dict).alert.to(FunctionVar).call(message))
  738. def set_focus(ref: str) -> EventSpec:
  739. """Set focus to specified ref.
  740. Args:
  741. ref: The ref.
  742. Returns:
  743. An event to set focus on the ref
  744. """
  745. return server_side(
  746. "_set_focus",
  747. get_fn_signature(set_focus),
  748. ref=LiteralVar.create(format.format_ref(ref)),
  749. )
  750. def scroll_to(elem_id: str, align_to_top: bool | Var[bool] = True) -> EventSpec:
  751. """Select the id of a html element for scrolling into view.
  752. Args:
  753. elem_id: The id of the element to scroll to.
  754. align_to_top: Whether to scroll to the top (True) or bottom (False) of the element.
  755. Returns:
  756. An EventSpec to scroll the page to the selected element.
  757. """
  758. get_element_by_id = FunctionStringVar.create("document.getElementById")
  759. return run_script(
  760. get_element_by_id.call(elem_id)
  761. .to(ObjectVar)
  762. .scrollIntoView.to(FunctionVar)
  763. .call(align_to_top),
  764. )
  765. def set_value(ref: str, value: Any) -> EventSpec:
  766. """Set the value of a ref.
  767. Args:
  768. ref: The ref.
  769. value: The value to set.
  770. Returns:
  771. An event to set the ref.
  772. """
  773. return server_side(
  774. "_set_value",
  775. get_fn_signature(set_value),
  776. ref=LiteralVar.create(format.format_ref(ref)),
  777. value=value,
  778. )
  779. def remove_cookie(key: str, options: dict[str, Any] | None = None) -> EventSpec:
  780. """Remove a cookie on the frontend.
  781. Args:
  782. key: The key identifying the cookie to be removed.
  783. options: Support all the cookie options from RFC 6265
  784. Returns:
  785. EventSpec: An event to remove a cookie.
  786. """
  787. options = options or {}
  788. options["path"] = options.get("path", "/")
  789. return server_side(
  790. "_remove_cookie",
  791. get_fn_signature(remove_cookie),
  792. key=key,
  793. options=options,
  794. )
  795. def clear_local_storage() -> EventSpec:
  796. """Set a value in the local storage on the frontend.
  797. Returns:
  798. EventSpec: An event to clear the local storage.
  799. """
  800. return server_side(
  801. "_clear_local_storage",
  802. get_fn_signature(clear_local_storage),
  803. )
  804. def remove_local_storage(key: str) -> EventSpec:
  805. """Set a value in the local storage on the frontend.
  806. Args:
  807. key: The key identifying the variable in the local storage to remove.
  808. Returns:
  809. EventSpec: An event to remove an item based on the provided key in local storage.
  810. """
  811. return server_side(
  812. "_remove_local_storage",
  813. get_fn_signature(remove_local_storage),
  814. key=key,
  815. )
  816. def clear_session_storage() -> EventSpec:
  817. """Set a value in the session storage on the frontend.
  818. Returns:
  819. EventSpec: An event to clear the session storage.
  820. """
  821. return server_side(
  822. "_clear_session_storage",
  823. get_fn_signature(clear_session_storage),
  824. )
  825. def remove_session_storage(key: str) -> EventSpec:
  826. """Set a value in the session storage on the frontend.
  827. Args:
  828. key: The key identifying the variable in the session storage to remove.
  829. Returns:
  830. EventSpec: An event to remove an item based on the provided key in session storage.
  831. """
  832. return server_side(
  833. "_remove_session_storage",
  834. get_fn_signature(remove_session_storage),
  835. key=key,
  836. )
  837. def set_clipboard(content: Union[str, Var[str]]) -> EventSpec:
  838. """Set the text in content in the clipboard.
  839. Args:
  840. content: The text to add to clipboard.
  841. Returns:
  842. EventSpec: An event to set some content in the clipboard.
  843. """
  844. return run_script(
  845. Var("navigator")
  846. .to(dict)
  847. .clipboard.to(dict)
  848. .writeText.to(FunctionVar)
  849. .call(content)
  850. )
  851. def download(
  852. url: str | Var | None = None,
  853. filename: Optional[str | Var] = None,
  854. data: str | bytes | Var | None = None,
  855. ) -> EventSpec:
  856. """Download the file at a given path or with the specified data.
  857. Args:
  858. url: The URL to the file to download.
  859. filename: The name that the file should be saved as after download.
  860. data: The data to download.
  861. Raises:
  862. ValueError: If the URL provided is invalid, both URL and data are provided,
  863. or the data is not an expected type.
  864. Returns:
  865. EventSpec: An event to download the associated file.
  866. """
  867. from reflex.components.core.cond import cond
  868. if isinstance(url, str):
  869. if not url.startswith("/"):
  870. raise ValueError("The URL argument should start with a /")
  871. # if filename is not provided, infer it from url
  872. if filename is None:
  873. filename = url.rpartition("/")[-1]
  874. if filename is None:
  875. filename = ""
  876. if data is not None:
  877. if url is not None:
  878. raise ValueError("Cannot provide both URL and data to download.")
  879. if isinstance(data, str):
  880. # Caller provided a plain text string to download.
  881. url = "data:text/plain," + urllib.parse.quote(data)
  882. elif isinstance(data, Var):
  883. # Need to check on the frontend if the Var already looks like a data: URI.
  884. is_data_url = (data.js_type() == "string") & (
  885. data.to(str).startswith("data:")
  886. ) # type: ignore
  887. # If it's a data: URI, use it as is, otherwise convert the Var to JSON in a data: URI.
  888. url = cond( # type: ignore
  889. is_data_url,
  890. data.to(str),
  891. "data:text/plain," + data.to_string(), # type: ignore
  892. )
  893. elif isinstance(data, bytes):
  894. # Caller provided bytes, so base64 encode it as a data: URI.
  895. b64_data = b64encode(data).decode("utf-8")
  896. url = "data:application/octet-stream;base64," + b64_data
  897. else:
  898. raise ValueError(
  899. f"Invalid data type {type(data)} for download. Use `str` or `bytes`."
  900. )
  901. return server_side(
  902. "_download",
  903. get_fn_signature(download),
  904. url=url,
  905. filename=filename,
  906. )
  907. def _callback_arg_spec(eval_result):
  908. """ArgSpec for call_script callback function.
  909. Args:
  910. eval_result: The result of the javascript execution.
  911. Returns:
  912. Args for the callback function
  913. """
  914. return [eval_result]
  915. def call_script(
  916. javascript_code: str | Var[str],
  917. callback: EventType | None = None,
  918. ) -> EventSpec:
  919. """Create an event handler that executes arbitrary javascript code.
  920. Args:
  921. javascript_code: The code to execute.
  922. callback: EventHandler that will receive the result of evaluating the javascript code.
  923. Returns:
  924. EventSpec: An event that will execute the client side javascript.
  925. """
  926. callback_kwargs = {}
  927. if callback is not None:
  928. callback_kwargs = {
  929. "callback": format.format_queue_events(
  930. callback,
  931. args_spec=lambda result: [result],
  932. )._js_expr,
  933. }
  934. if isinstance(javascript_code, str):
  935. # When there is VarData, include it and eval the JS code inline on the client.
  936. javascript_code, original_code = (
  937. LiteralVar.create(javascript_code),
  938. javascript_code,
  939. )
  940. if not javascript_code._get_all_var_data():
  941. # Without VarData, cast to string and eval the code in the event loop.
  942. javascript_code = str(Var(_js_expr=original_code))
  943. return server_side(
  944. "_call_script",
  945. get_fn_signature(call_script),
  946. javascript_code=javascript_code,
  947. **callback_kwargs,
  948. )
  949. def call_function(
  950. javascript_code: str | Var,
  951. callback: EventType | None = None,
  952. ) -> EventSpec:
  953. """Create an event handler that executes arbitrary javascript code.
  954. Args:
  955. javascript_code: The code to execute.
  956. callback: EventHandler that will receive the result of evaluating the javascript code.
  957. Returns:
  958. EventSpec: An event that will execute the client side javascript.
  959. """
  960. callback_kwargs = {"callback": None}
  961. if callback is not None:
  962. callback_kwargs = {
  963. "callback": format.format_queue_events(
  964. callback,
  965. args_spec=lambda result: [result],
  966. ),
  967. }
  968. javascript_code = (
  969. Var(javascript_code) if isinstance(javascript_code, str) else javascript_code
  970. )
  971. return server_side(
  972. "_call_function",
  973. get_fn_signature(call_function),
  974. function=javascript_code,
  975. **callback_kwargs,
  976. )
  977. def run_script(
  978. javascript_code: str | Var,
  979. callback: EventType | None = None,
  980. ) -> EventSpec:
  981. """Create an event handler that executes arbitrary javascript code.
  982. Args:
  983. javascript_code: The code to execute.
  984. callback: EventHandler that will receive the result of evaluating the javascript code.
  985. Returns:
  986. EventSpec: An event that will execute the client side javascript.
  987. """
  988. javascript_code = (
  989. Var(javascript_code) if isinstance(javascript_code, str) else javascript_code
  990. )
  991. return call_function(ArgsFunctionOperation.create((), javascript_code), callback)
  992. def get_event(state, event):
  993. """Get the event from the given state.
  994. Args:
  995. state: The state.
  996. event: The event.
  997. Returns:
  998. The event.
  999. """
  1000. return f"{state.get_name()}.{event}"
  1001. def get_hydrate_event(state) -> str:
  1002. """Get the name of the hydrate event for the state.
  1003. Args:
  1004. state: The state.
  1005. Returns:
  1006. The name of the hydrate event.
  1007. """
  1008. return get_event(state, constants.CompileVars.HYDRATE)
  1009. def call_event_handler(
  1010. event_callback: EventHandler | EventSpec,
  1011. event_spec: ArgsSpec | Sequence[ArgsSpec],
  1012. key: Optional[str] = None,
  1013. ) -> EventSpec:
  1014. """Call an event handler to get the event spec.
  1015. This function will inspect the function signature of the event handler.
  1016. If it takes in an arg, the arg will be passed to the event handler.
  1017. Otherwise, the event handler will be called with no args.
  1018. Args:
  1019. event_callback: The event handler.
  1020. event_spec: The lambda that define the argument(s) to pass to the event handler.
  1021. key: The key to pass to the event handler.
  1022. Returns:
  1023. The event spec from calling the event handler.
  1024. # noqa: DAR401 failure
  1025. """
  1026. event_spec_args = parse_args_spec(event_spec) # type: ignore
  1027. if isinstance(event_callback, EventSpec):
  1028. check_fn_match_arg_spec(
  1029. event_callback.handler.fn,
  1030. event_spec,
  1031. key,
  1032. bool(event_callback.handler.state_full_name) + len(event_callback.args),
  1033. event_callback.handler.fn.__qualname__,
  1034. )
  1035. # Handle partial application of EventSpec args
  1036. return event_callback.add_args(*event_spec_args)
  1037. check_fn_match_arg_spec(
  1038. event_callback.fn,
  1039. event_spec,
  1040. key,
  1041. bool(event_callback.state_full_name),
  1042. event_callback.fn.__qualname__,
  1043. )
  1044. all_acceptable_specs = (
  1045. [event_spec] if not isinstance(event_spec, Sequence) else event_spec
  1046. )
  1047. event_spec_return_types = list(
  1048. filter(
  1049. lambda event_spec_return_type: event_spec_return_type is not None
  1050. and get_origin(event_spec_return_type) is tuple,
  1051. (
  1052. get_type_hints(arg_spec).get("return", None)
  1053. for arg_spec in all_acceptable_specs
  1054. ),
  1055. )
  1056. )
  1057. if event_spec_return_types:
  1058. failures = []
  1059. event_callback_spec = inspect.getfullargspec(event_callback.fn)
  1060. for event_spec_index, event_spec_return_type in enumerate(
  1061. event_spec_return_types
  1062. ):
  1063. args = get_args(event_spec_return_type)
  1064. args_types_without_vars = [
  1065. arg if get_origin(arg) is not Var else get_args(arg)[0] for arg in args
  1066. ]
  1067. try:
  1068. type_hints_of_provided_callback = get_type_hints(event_callback.fn)
  1069. except NameError:
  1070. type_hints_of_provided_callback = {}
  1071. failed_type_check = False
  1072. # check that args of event handler are matching the spec if type hints are provided
  1073. for i, arg in enumerate(event_callback_spec.args[1:]):
  1074. if arg not in type_hints_of_provided_callback:
  1075. continue
  1076. try:
  1077. compare_result = typehint_issubclass(
  1078. args_types_without_vars[i], type_hints_of_provided_callback[arg]
  1079. )
  1080. except TypeError:
  1081. # TODO: In 0.7.0, remove this block and raise the exception
  1082. # raise TypeError(
  1083. # f"Could not compare types {args_types_without_vars[i]} and {type_hints_of_provided_callback[arg]} for argument {arg} of {event_handler.fn.__qualname__} provided for {key}." # noqa: ERA001
  1084. # ) from e
  1085. console.warn(
  1086. f"Could not compare types {args_types_without_vars[i]} and {type_hints_of_provided_callback[arg]} for argument {arg} of {event_callback.fn.__qualname__} provided for {key}."
  1087. )
  1088. compare_result = False
  1089. if compare_result:
  1090. continue
  1091. else:
  1092. failure = EventHandlerArgTypeMismatch(
  1093. f"Event handler {key} expects {args_types_without_vars[i]} for argument {arg} but got {type_hints_of_provided_callback[arg]} as annotated in {event_callback.fn.__qualname__} instead."
  1094. )
  1095. failures.append(failure)
  1096. failed_type_check = True
  1097. break
  1098. if not failed_type_check:
  1099. if event_spec_index:
  1100. args = get_args(event_spec_return_types[0])
  1101. args_types_without_vars = [
  1102. arg if get_origin(arg) is not Var else get_args(arg)[0]
  1103. for arg in args
  1104. ]
  1105. expect_string = ", ".join(
  1106. repr(arg) for arg in args_types_without_vars
  1107. ).replace("[", "\\[")
  1108. given_string = ", ".join(
  1109. repr(type_hints_of_provided_callback.get(arg, Any))
  1110. for arg in event_callback_spec.args[1:]
  1111. ).replace("[", "\\[")
  1112. console.warn(
  1113. f"Event handler {key} expects ({expect_string}) -> () but got ({given_string}) -> () as annotated in {event_callback.fn.__qualname__} instead. "
  1114. f"This may lead to unexpected behavior but is intentionally ignored for {key}."
  1115. )
  1116. return event_callback(*event_spec_args)
  1117. if failures:
  1118. console.deprecate(
  1119. "Mismatched event handler argument types",
  1120. "\n".join([str(f) for f in failures]),
  1121. "0.6.5",
  1122. "0.7.0",
  1123. )
  1124. return event_callback(*event_spec_args) # type: ignore
  1125. def unwrap_var_annotation(annotation: GenericType):
  1126. """Unwrap a Var annotation or return it as is if it's not Var[X].
  1127. Args:
  1128. annotation: The annotation to unwrap.
  1129. Returns:
  1130. The unwrapped annotation.
  1131. """
  1132. if get_origin(annotation) is Var and (args := get_args(annotation)):
  1133. return args[0]
  1134. return annotation
  1135. def resolve_annotation(annotations: dict[str, Any], arg_name: str):
  1136. """Resolve the annotation for the given argument name.
  1137. Args:
  1138. annotations: The annotations.
  1139. arg_name: The argument name.
  1140. Returns:
  1141. The resolved annotation.
  1142. """
  1143. annotation = annotations.get(arg_name)
  1144. if annotation is None:
  1145. console.deprecate(
  1146. feature_name="Unannotated event handler arguments",
  1147. reason="Provide type annotations for event handler arguments.",
  1148. deprecation_version="0.6.3",
  1149. removal_version="0.7.0",
  1150. )
  1151. # Allow arbitrary attribute access two levels deep until removed.
  1152. return Dict[str, dict]
  1153. return annotation
  1154. def parse_args_spec(arg_spec: ArgsSpec | Sequence[ArgsSpec]):
  1155. """Parse the args provided in the ArgsSpec of an event trigger.
  1156. Args:
  1157. arg_spec: The spec of the args.
  1158. Returns:
  1159. The parsed args.
  1160. """
  1161. # if there's multiple, the first is the default
  1162. arg_spec = arg_spec[0] if isinstance(arg_spec, Sequence) else arg_spec
  1163. spec = inspect.getfullargspec(arg_spec)
  1164. annotations = get_type_hints(arg_spec)
  1165. return list(
  1166. arg_spec(
  1167. *[
  1168. Var(f"_{l_arg}").to(
  1169. unwrap_var_annotation(resolve_annotation(annotations, l_arg))
  1170. )
  1171. for l_arg in spec.args
  1172. ]
  1173. )
  1174. )
  1175. def check_fn_match_arg_spec(
  1176. user_func: Callable,
  1177. arg_spec: ArgsSpec | Sequence[ArgsSpec],
  1178. key: str | None = None,
  1179. number_of_bound_args: int = 0,
  1180. func_name: str | None = None,
  1181. ):
  1182. """Ensures that the function signature matches the passed argument specification
  1183. or raises an EventFnArgMismatch if they do not.
  1184. Args:
  1185. user_func: The function to be validated.
  1186. arg_spec: The argument specification for the event trigger.
  1187. key: The key of the event trigger.
  1188. number_of_bound_args: The number of bound arguments to the function.
  1189. func_name: The name of the function to be validated.
  1190. Raises:
  1191. EventFnArgMismatch: Raised if the number of mandatory arguments do not match
  1192. """
  1193. user_args = inspect.getfullargspec(user_func).args
  1194. # Drop the first argument if it's a bound method
  1195. if inspect.ismethod(user_func) and user_func.__self__ is not None:
  1196. user_args = user_args[1:]
  1197. user_default_args = inspect.getfullargspec(user_func).defaults
  1198. number_of_user_args = len(user_args) - number_of_bound_args
  1199. number_of_user_default_args = len(user_default_args) if user_default_args else 0
  1200. parsed_event_args = parse_args_spec(arg_spec)
  1201. number_of_event_args = len(parsed_event_args)
  1202. if number_of_user_args - number_of_user_default_args > number_of_event_args:
  1203. raise EventFnArgMismatch(
  1204. f"Event {key} only provides {number_of_event_args} arguments, but "
  1205. f"{func_name or user_func} requires at least {number_of_user_args - number_of_user_default_args} "
  1206. "arguments to be passed to the event handler.\n"
  1207. "See https://reflex.dev/docs/events/event-arguments/"
  1208. )
  1209. def call_event_fn(
  1210. fn: Callable,
  1211. arg_spec: ArgsSpec | Sequence[ArgsSpec],
  1212. key: Optional[str] = None,
  1213. ) -> list[EventSpec] | Var:
  1214. """Call a function to a list of event specs.
  1215. The function should return a single EventSpec, a list of EventSpecs, or a
  1216. single Var.
  1217. Args:
  1218. fn: The function to call.
  1219. arg_spec: The argument spec for the event trigger.
  1220. key: The key to pass to the event handler.
  1221. Returns:
  1222. The event specs from calling the function or a Var.
  1223. Raises:
  1224. EventHandlerValueError: If the lambda returns an unusable value.
  1225. """
  1226. # Import here to avoid circular imports.
  1227. from reflex.event import EventHandler, EventSpec
  1228. from reflex.utils.exceptions import EventHandlerValueError
  1229. # Check that fn signature matches arg_spec
  1230. check_fn_match_arg_spec(fn, arg_spec, key=key)
  1231. parsed_args = parse_args_spec(arg_spec)
  1232. number_of_fn_args = len(inspect.getfullargspec(fn).args)
  1233. # Call the function with the parsed args.
  1234. out = fn(*[*parsed_args][:number_of_fn_args])
  1235. # If the function returns a Var, assume it's an EventChain and render it directly.
  1236. if isinstance(out, Var):
  1237. return out
  1238. # Convert the output to a list.
  1239. if not isinstance(out, list):
  1240. out = [out]
  1241. # Convert any event specs to event specs.
  1242. events = []
  1243. for e in out:
  1244. if isinstance(e, EventHandler):
  1245. # An un-called EventHandler gets all of the args of the event trigger.
  1246. e = call_event_handler(e, arg_spec, key=key)
  1247. # Make sure the event spec is valid.
  1248. if not isinstance(e, EventSpec):
  1249. raise EventHandlerValueError(
  1250. f"Lambda {fn} returned an invalid event spec: {e}."
  1251. )
  1252. # Add the event spec to the chain.
  1253. events.append(e)
  1254. # Return the events.
  1255. return events
  1256. def get_handler_args(
  1257. event_spec: EventSpec,
  1258. ) -> tuple[tuple[Var, Var], ...]:
  1259. """Get the handler args for the given event spec.
  1260. Args:
  1261. event_spec: The event spec.
  1262. Returns:
  1263. The handler args.
  1264. """
  1265. args = inspect.getfullargspec(event_spec.handler.fn).args
  1266. return event_spec.args if len(args) > 1 else ()
  1267. def fix_events(
  1268. events: list[EventHandler | EventSpec] | None,
  1269. token: str,
  1270. router_data: dict[str, Any] | None = None,
  1271. ) -> list[Event]:
  1272. """Fix a list of events returned by an event handler.
  1273. Args:
  1274. events: The events to fix.
  1275. token: The user token.
  1276. router_data: The optional router data to set in the event.
  1277. Raises:
  1278. ValueError: If the event type is not what was expected.
  1279. Returns:
  1280. The fixed events.
  1281. """
  1282. # If the event handler returns nothing, return an empty list.
  1283. if events is None:
  1284. return []
  1285. # If the handler returns a single event, wrap it in a list.
  1286. if not isinstance(events, List):
  1287. events = [events]
  1288. # Fix the events created by the handler.
  1289. out = []
  1290. for e in events:
  1291. if isinstance(e, Event):
  1292. # If the event is already an event, append it to the list.
  1293. out.append(e)
  1294. continue
  1295. if not isinstance(e, (EventHandler, EventSpec)):
  1296. e = EventHandler(fn=e)
  1297. # Otherwise, create an event from the event spec.
  1298. if isinstance(e, EventHandler):
  1299. e = e()
  1300. if not isinstance(e, EventSpec):
  1301. raise ValueError(f"Unexpected event type, {type(e)}.")
  1302. name = format.format_event_handler(e.handler)
  1303. payload = {k._js_expr: v._decode() for k, v in e.args} # type: ignore
  1304. # Filter router_data to reduce payload size
  1305. event_router_data = {
  1306. k: v
  1307. for k, v in (router_data or {}).items()
  1308. if k in constants.route.ROUTER_DATA_INCLUDE
  1309. }
  1310. # Create an event and append it to the list.
  1311. out.append(
  1312. Event(
  1313. token=token,
  1314. name=name,
  1315. payload=payload,
  1316. router_data=event_router_data,
  1317. )
  1318. )
  1319. return out
  1320. def get_fn_signature(fn: Callable) -> inspect.Signature:
  1321. """Get the signature of a function.
  1322. Args:
  1323. fn: The function.
  1324. Returns:
  1325. The signature of the function.
  1326. """
  1327. signature = inspect.signature(fn)
  1328. new_param = inspect.Parameter(
  1329. FRONTEND_EVENT_STATE, inspect.Parameter.POSITIONAL_OR_KEYWORD, annotation=Any
  1330. )
  1331. return signature.replace(parameters=(new_param, *signature.parameters.values()))
  1332. class EventVar(ObjectVar, python_types=EventSpec):
  1333. """Base class for event vars."""
  1334. @dataclasses.dataclass(
  1335. eq=False,
  1336. frozen=True,
  1337. **{"slots": True} if sys.version_info >= (3, 10) else {},
  1338. )
  1339. class LiteralEventVar(VarOperationCall, LiteralVar, EventVar):
  1340. """A literal event var."""
  1341. _var_value: EventSpec = dataclasses.field(default=None) # type: ignore
  1342. def __hash__(self) -> int:
  1343. """Get the hash of the var.
  1344. Returns:
  1345. The hash of the var.
  1346. """
  1347. return hash((type(self).__name__, self._js_expr))
  1348. @classmethod
  1349. def create(
  1350. cls,
  1351. value: EventSpec,
  1352. _var_data: VarData | None = None,
  1353. ) -> LiteralEventVar:
  1354. """Create a new LiteralEventVar instance.
  1355. Args:
  1356. value: The value of the var.
  1357. _var_data: The data of the var.
  1358. Returns:
  1359. The created LiteralEventVar instance.
  1360. """
  1361. return cls(
  1362. _js_expr="",
  1363. _var_type=EventSpec,
  1364. _var_data=_var_data,
  1365. _var_value=value,
  1366. _func=FunctionStringVar("Event"),
  1367. _args=(
  1368. # event handler name
  1369. ".".join(
  1370. filter(
  1371. None,
  1372. format.get_event_handler_parts(value.handler),
  1373. )
  1374. ),
  1375. # event handler args
  1376. {str(name): value for name, value in value.args},
  1377. # event actions
  1378. value.event_actions,
  1379. # client handler name
  1380. *([value.client_handler_name] if value.client_handler_name else []),
  1381. ),
  1382. )
  1383. class EventChainVar(BuilderFunctionVar, python_types=EventChain):
  1384. """Base class for event chain vars."""
  1385. @dataclasses.dataclass(
  1386. eq=False,
  1387. frozen=True,
  1388. **{"slots": True} if sys.version_info >= (3, 10) else {},
  1389. )
  1390. # Note: LiteralVar is second in the inheritance list allowing it act like a
  1391. # CachedVarOperation (ArgsFunctionOperation) and get the _js_expr from the
  1392. # _cached_var_name property.
  1393. class LiteralEventChainVar(ArgsFunctionOperationBuilder, LiteralVar, EventChainVar):
  1394. """A literal event chain var."""
  1395. _var_value: EventChain = dataclasses.field(default=None) # type: ignore
  1396. def __hash__(self) -> int:
  1397. """Get the hash of the var.
  1398. Returns:
  1399. The hash of the var.
  1400. """
  1401. return hash((type(self).__name__, self._js_expr))
  1402. @classmethod
  1403. def create(
  1404. cls,
  1405. value: EventChain,
  1406. _var_data: VarData | None = None,
  1407. ) -> LiteralEventChainVar:
  1408. """Create a new LiteralEventChainVar instance.
  1409. Args:
  1410. value: The value of the var.
  1411. _var_data: The data of the var.
  1412. Returns:
  1413. The created LiteralEventChainVar instance.
  1414. """
  1415. arg_spec = (
  1416. value.args_spec[0]
  1417. if isinstance(value.args_spec, Sequence)
  1418. else value.args_spec
  1419. )
  1420. sig = inspect.signature(arg_spec) # type: ignore
  1421. if sig.parameters:
  1422. arg_def = tuple((f"_{p}" for p in sig.parameters))
  1423. arg_def_expr = LiteralVar.create([Var(_js_expr=arg) for arg in arg_def])
  1424. else:
  1425. # add a default argument for addEvents if none were specified in value.args_spec
  1426. # used to trigger the preventDefault() on the event.
  1427. arg_def = ("...args",)
  1428. arg_def_expr = Var(_js_expr="args")
  1429. if value.invocation is None:
  1430. invocation = FunctionStringVar.create("addEvents")
  1431. else:
  1432. invocation = value.invocation
  1433. return cls(
  1434. _js_expr="",
  1435. _var_type=EventChain,
  1436. _var_data=_var_data,
  1437. _args=FunctionArgs(arg_def),
  1438. _return_expr=invocation.call(
  1439. LiteralVar.create([LiteralVar.create(event) for event in value.events]),
  1440. arg_def_expr,
  1441. value.event_actions,
  1442. ),
  1443. _var_value=value,
  1444. )
  1445. P = ParamSpec("P")
  1446. Q = ParamSpec("Q")
  1447. T = TypeVar("T")
  1448. V = TypeVar("V")
  1449. V2 = TypeVar("V2")
  1450. V3 = TypeVar("V3")
  1451. V4 = TypeVar("V4")
  1452. V5 = TypeVar("V5")
  1453. background_event_decorator = background
  1454. class EventCallback(Generic[P, T]):
  1455. """A descriptor that wraps a function to be used as an event."""
  1456. def __init__(self, func: Callable[Concatenate[Any, P], T]):
  1457. """Initialize the descriptor with the function to be wrapped.
  1458. Args:
  1459. func: The function to be wrapped.
  1460. """
  1461. self.func = func
  1462. def throttle(self, limit_ms: int):
  1463. """Throttle the event handler.
  1464. Args:
  1465. limit_ms: The time in milliseconds to throttle the event handler.
  1466. Returns:
  1467. New EventHandler-like with throttle set to limit_ms.
  1468. """
  1469. return self
  1470. def debounce(self, delay_ms: int):
  1471. """Debounce the event handler.
  1472. Args:
  1473. delay_ms: The time in milliseconds to debounce the event handler.
  1474. Returns:
  1475. New EventHandler-like with debounce set to delay_ms.
  1476. """
  1477. return self
  1478. @property
  1479. def temporal(self):
  1480. """Do not queue the event if the backend is down.
  1481. Returns:
  1482. New EventHandler-like with temporal set to True.
  1483. """
  1484. return self
  1485. @property
  1486. def prevent_default(self):
  1487. """Prevent default behavior.
  1488. Returns:
  1489. The event callback with prevent default behavior.
  1490. """
  1491. return self
  1492. @property
  1493. def stop_propagation(self):
  1494. """Stop event propagation.
  1495. Returns:
  1496. The event callback with stop propagation behavior.
  1497. """
  1498. return self
  1499. @overload
  1500. def __call__(
  1501. self: EventCallback[Q, T],
  1502. ) -> EventCallback[Q, T]: ...
  1503. @overload
  1504. def __call__(
  1505. self: EventCallback[Concatenate[V, Q], T], value: V | Var[V]
  1506. ) -> EventCallback[Q, T]: ...
  1507. @overload
  1508. def __call__(
  1509. self: EventCallback[Concatenate[V, V2, Q], T],
  1510. value: V | Var[V],
  1511. value2: V2 | Var[V2],
  1512. ) -> EventCallback[Q, T]: ...
  1513. @overload
  1514. def __call__(
  1515. self: EventCallback[Concatenate[V, V2, V3, Q], T],
  1516. value: V | Var[V],
  1517. value2: V2 | Var[V2],
  1518. value3: V3 | Var[V3],
  1519. ) -> EventCallback[Q, T]: ...
  1520. @overload
  1521. def __call__(
  1522. self: EventCallback[Concatenate[V, V2, V3, V4, Q], T],
  1523. value: V | Var[V],
  1524. value2: V2 | Var[V2],
  1525. value3: V3 | Var[V3],
  1526. value4: V4 | Var[V4],
  1527. ) -> EventCallback[Q, T]: ...
  1528. def __call__(self, *values) -> EventCallback: # type: ignore
  1529. """Call the function with the values.
  1530. Args:
  1531. *values: The values to call the function with.
  1532. Returns:
  1533. The function with the values.
  1534. """
  1535. return self.func(*values) # type: ignore
  1536. @overload
  1537. def __get__(
  1538. self: EventCallback[P, T], instance: None, owner
  1539. ) -> EventCallback[P, T]: ...
  1540. @overload
  1541. def __get__(self, instance, owner) -> Callable[P, T]: ...
  1542. def __get__(self, instance, owner) -> Callable: # type: ignore
  1543. """Get the function with the instance bound to it.
  1544. Args:
  1545. instance: The instance to bind to the function.
  1546. owner: The owner of the function.
  1547. Returns:
  1548. The function with the instance bound to it
  1549. """
  1550. if instance is None:
  1551. return self.func # type: ignore
  1552. return partial(self.func, instance) # type: ignore
  1553. G = ParamSpec("G")
  1554. if TYPE_CHECKING:
  1555. from reflex.state import BaseState
  1556. BASE_STATE = TypeVar("BASE_STATE", bound=BaseState)
  1557. else:
  1558. BASE_STATE = TypeVar("BASE_STATE")
  1559. StateCallable = TypeAliasType(
  1560. "StateCallable",
  1561. Callable[Concatenate[BASE_STATE, G], Any],
  1562. type_params=(G, BASE_STATE),
  1563. )
  1564. IndividualEventType = Union[
  1565. EventSpec,
  1566. EventHandler,
  1567. Callable[G, Any],
  1568. StateCallable[G, BASE_STATE],
  1569. EventCallback[G, Any],
  1570. Var[Any],
  1571. ]
  1572. ItemOrList = Union[V, List[V]]
  1573. EventType = ItemOrList[IndividualEventType[G, BASE_STATE]]
  1574. class EventNamespace(types.SimpleNamespace):
  1575. """A namespace for event related classes."""
  1576. Event = Event
  1577. EventHandler = EventHandler
  1578. EventSpec = EventSpec
  1579. CallableEventSpec = CallableEventSpec
  1580. EventChain = EventChain
  1581. EventVar = EventVar
  1582. LiteralEventVar = LiteralEventVar
  1583. EventChainVar = EventChainVar
  1584. LiteralEventChainVar = LiteralEventChainVar
  1585. EventType = EventType
  1586. EventCallback = EventCallback
  1587. @overload
  1588. @staticmethod
  1589. def __call__(
  1590. func: None = None, *, background: bool | None = None
  1591. ) -> Callable[[Callable[Concatenate[BASE_STATE, P], T]], EventCallback[P, T]]: ...
  1592. @overload
  1593. @staticmethod
  1594. def __call__(
  1595. func: Callable[Concatenate[BASE_STATE, P], T],
  1596. *,
  1597. background: bool | None = None,
  1598. ) -> EventCallback[P, T]: ...
  1599. @staticmethod
  1600. def __call__(
  1601. func: Callable[Concatenate[BASE_STATE, P], T] | None = None,
  1602. *,
  1603. background: bool | None = None,
  1604. ) -> Union[
  1605. EventCallback[P, T],
  1606. Callable[[Callable[Concatenate[BASE_STATE, P], T]], EventCallback[P, T]],
  1607. ]:
  1608. """Wrap a function to be used as an event.
  1609. Args:
  1610. func: The function to wrap.
  1611. background: Whether the event should be run in the background. Defaults to False.
  1612. Returns:
  1613. The wrapped function.
  1614. """
  1615. def wrapper(
  1616. func: Callable[Concatenate[BASE_STATE, P], T],
  1617. ) -> EventCallback[P, T]:
  1618. if background is True:
  1619. return background_event_decorator(func, __internal_reflex_call=True) # type: ignore
  1620. return func # type: ignore
  1621. if func is not None:
  1622. return wrapper(func)
  1623. return wrapper
  1624. get_event = staticmethod(get_event)
  1625. get_hydrate_event = staticmethod(get_hydrate_event)
  1626. fix_events = staticmethod(fix_events)
  1627. call_event_handler = staticmethod(call_event_handler)
  1628. call_event_fn = staticmethod(call_event_fn)
  1629. get_handler_args = staticmethod(get_handler_args)
  1630. check_fn_match_arg_spec = staticmethod(check_fn_match_arg_spec)
  1631. resolve_annotation = staticmethod(resolve_annotation)
  1632. parse_args_spec = staticmethod(parse_args_spec)
  1633. passthrough_event_spec = staticmethod(passthrough_event_spec)
  1634. input_event = staticmethod(input_event)
  1635. key_event = staticmethod(key_event)
  1636. no_args_event_spec = staticmethod(no_args_event_spec)
  1637. server_side = staticmethod(server_side)
  1638. redirect = staticmethod(redirect)
  1639. console_log = staticmethod(console_log)
  1640. noop = staticmethod(noop)
  1641. back = staticmethod(back)
  1642. window_alert = staticmethod(window_alert)
  1643. set_focus = staticmethod(set_focus)
  1644. scroll_to = staticmethod(scroll_to)
  1645. set_value = staticmethod(set_value)
  1646. remove_cookie = staticmethod(remove_cookie)
  1647. clear_local_storage = staticmethod(clear_local_storage)
  1648. remove_local_storage = staticmethod(remove_local_storage)
  1649. clear_session_storage = staticmethod(clear_session_storage)
  1650. remove_session_storage = staticmethod(remove_session_storage)
  1651. set_clipboard = staticmethod(set_clipboard)
  1652. download = staticmethod(download)
  1653. call_script = staticmethod(call_script)
  1654. call_function = staticmethod(call_function)
  1655. run_script = staticmethod(run_script)
  1656. event = EventNamespace()