reflex 0.6.8a1__py3-none-any.whl → 0.7.0a1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of reflex might be problematic. Click here for more details.

Files changed (155) hide show
  1. reflex/.templates/jinja/custom_components/pyproject.toml.jinja2 +1 -1
  2. reflex/.templates/jinja/web/pages/_app.js.jinja2 +7 -7
  3. reflex/.templates/jinja/web/pages/utils.js.jinja2 +2 -2
  4. reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js +1 -4
  5. reflex/.templates/web/utils/state.js +65 -36
  6. reflex/__init__.py +4 -17
  7. reflex/__init__.pyi +1 -2
  8. reflex/app.py +244 -115
  9. reflex/app_mixins/lifespan.py +9 -9
  10. reflex/app_mixins/middleware.py +6 -6
  11. reflex/app_module_for_backend.py +3 -7
  12. reflex/base.py +7 -7
  13. reflex/compiler/compiler.py +8 -0
  14. reflex/compiler/utils.py +35 -6
  15. reflex/components/base/bare.py +1 -1
  16. reflex/components/base/error_boundary.py +2 -1
  17. reflex/components/base/error_boundary.pyi +2 -1
  18. reflex/components/base/meta.py +2 -2
  19. reflex/components/base/strict_mode.py +10 -0
  20. reflex/components/base/strict_mode.pyi +57 -0
  21. reflex/components/component.py +38 -77
  22. reflex/components/core/banner.py +83 -4
  23. reflex/components/core/banner.pyi +86 -0
  24. reflex/components/core/breakpoints.py +3 -1
  25. reflex/components/core/client_side_routing.py +1 -1
  26. reflex/components/core/client_side_routing.pyi +1 -1
  27. reflex/components/core/cond.py +9 -10
  28. reflex/components/core/debounce.py +1 -1
  29. reflex/components/core/foreach.py +23 -3
  30. reflex/components/core/html.py +1 -1
  31. reflex/components/core/match.py +5 -5
  32. reflex/components/core/sticky.py +160 -0
  33. reflex/components/core/sticky.pyi +449 -0
  34. reflex/components/core/upload.py +2 -2
  35. reflex/components/datadisplay/code.py +5 -14
  36. reflex/components/datadisplay/dataeditor.py +7 -4
  37. reflex/components/datadisplay/logo.py +13 -8
  38. reflex/components/datadisplay/shiki_code_block.py +14 -9
  39. reflex/components/dynamic.py +22 -3
  40. reflex/components/el/constants/reflex.py +1 -1
  41. reflex/components/el/element.py +1 -1
  42. reflex/components/el/elements/forms.py +4 -4
  43. reflex/components/el/elements/forms.pyi +4 -4
  44. reflex/components/lucide/icon.py +46 -8
  45. reflex/components/lucide/icon.pyi +54 -0
  46. reflex/components/markdown/markdown.py +10 -8
  47. reflex/components/moment/moment.py +2 -2
  48. reflex/components/next/image.py +16 -4
  49. reflex/components/next/image.pyi +4 -2
  50. reflex/components/next/link.py +1 -1
  51. reflex/components/plotly/plotly.py +5 -5
  52. reflex/components/props.py +3 -3
  53. reflex/components/radix/__init__.pyi +1 -1
  54. reflex/components/radix/primitives/accordion.py +9 -5
  55. reflex/components/radix/primitives/accordion.pyi +3 -1
  56. reflex/components/radix/primitives/drawer.py +5 -2
  57. reflex/components/radix/primitives/drawer.pyi +4 -4
  58. reflex/components/radix/primitives/form.pyi +6 -6
  59. reflex/components/radix/primitives/progress.py +1 -1
  60. reflex/components/radix/primitives/slider.py +1 -1
  61. reflex/components/radix/themes/color_mode.py +11 -9
  62. reflex/components/radix/themes/components/alert_dialog.py +3 -0
  63. reflex/components/radix/themes/components/card.py +1 -1
  64. reflex/components/radix/themes/components/card.pyi +1 -1
  65. reflex/components/radix/themes/components/context_menu.py +5 -0
  66. reflex/components/radix/themes/components/dialog.py +3 -0
  67. reflex/components/radix/themes/components/dropdown_menu.py +5 -0
  68. reflex/components/radix/themes/components/hover_card.py +3 -0
  69. reflex/components/radix/themes/components/icon_button.py +2 -2
  70. reflex/components/radix/themes/components/icon_button.pyi +1 -0
  71. reflex/components/radix/themes/components/popover.py +3 -0
  72. reflex/components/radix/themes/components/radio_cards.py +2 -0
  73. reflex/components/radix/themes/components/radio_group.py +1 -1
  74. reflex/components/radix/themes/components/select.py +3 -0
  75. reflex/components/radix/themes/components/tabs.py +3 -0
  76. reflex/components/radix/themes/components/text_area.py +12 -0
  77. reflex/components/radix/themes/components/text_area.pyi +2 -0
  78. reflex/components/radix/themes/components/text_field.py +1 -1
  79. reflex/components/radix/themes/components/tooltip.py +3 -1
  80. reflex/components/radix/themes/components/tooltip.pyi +1 -0
  81. reflex/components/radix/themes/layout/__init__.pyi +1 -1
  82. reflex/components/radix/themes/layout/list.py +2 -2
  83. reflex/components/radix/themes/layout/stack.py +2 -2
  84. reflex/components/radix/themes/typography/link.py +1 -1
  85. reflex/components/radix/themes/typography/text.py +2 -2
  86. reflex/components/react_player/react_player.py +1 -1
  87. reflex/components/recharts/__init__.py +2 -0
  88. reflex/components/recharts/__init__.pyi +2 -0
  89. reflex/components/recharts/charts.py +15 -15
  90. reflex/components/recharts/general.py +19 -4
  91. reflex/components/recharts/general.pyi +55 -4
  92. reflex/components/recharts/polar.py +2 -2
  93. reflex/components/recharts/recharts.py +4 -4
  94. reflex/components/sonner/toast.py +15 -13
  95. reflex/components/sonner/toast.pyi +6 -6
  96. reflex/components/suneditor/editor.py +6 -4
  97. reflex/components/suneditor/editor.pyi +2 -2
  98. reflex/components/tags/iter_tag.py +3 -3
  99. reflex/components/tags/tag.py +25 -3
  100. reflex/config.py +48 -20
  101. reflex/constants/__init__.py +1 -0
  102. reflex/constants/base.py +4 -1
  103. reflex/constants/compiler.py +5 -2
  104. reflex/constants/config.py +8 -1
  105. reflex/constants/installer.py +9 -9
  106. reflex/constants/style.py +1 -1
  107. reflex/custom_components/custom_components.py +9 -7
  108. reflex/event.py +137 -163
  109. reflex/experimental/__init__.py +19 -11
  110. reflex/experimental/client_state.py +53 -28
  111. reflex/experimental/hooks.py +5 -5
  112. reflex/experimental/layout.py +8 -5
  113. reflex/experimental/layout.pyi +1 -1
  114. reflex/experimental/misc.py +3 -3
  115. reflex/istate/wrappers.py +1 -1
  116. reflex/middleware/hydrate_middleware.py +2 -2
  117. reflex/model.py +11 -6
  118. reflex/page.py +3 -3
  119. reflex/reflex.py +90 -19
  120. reflex/route.py +1 -1
  121. reflex/state.py +358 -401
  122. reflex/style.py +27 -3
  123. reflex/testing.py +34 -39
  124. reflex/utils/build.py +6 -2
  125. reflex/utils/codespaces.py +1 -4
  126. reflex/utils/compat.py +6 -5
  127. reflex/utils/console.py +52 -21
  128. reflex/utils/exceptions.py +76 -26
  129. reflex/utils/exec.py +69 -74
  130. reflex/utils/export.py +6 -1
  131. reflex/utils/format.py +7 -39
  132. reflex/utils/imports.py +2 -2
  133. reflex/utils/lazy_loader.py +7 -1
  134. reflex/utils/path_ops.py +28 -14
  135. reflex/utils/prerequisites.py +324 -65
  136. reflex/utils/processes.py +45 -32
  137. reflex/utils/pyi_generator.py +30 -25
  138. reflex/utils/registry.py +4 -4
  139. reflex/utils/serializers.py +1 -1
  140. reflex/utils/telemetry.py +5 -4
  141. reflex/utils/types.py +42 -18
  142. reflex/vars/base.py +650 -333
  143. reflex/vars/datetime.py +6 -7
  144. reflex/vars/dep_tracking.py +344 -0
  145. reflex/vars/function.py +11 -5
  146. reflex/vars/number.py +31 -43
  147. reflex/vars/object.py +63 -62
  148. reflex/vars/sequence.py +79 -67
  149. {reflex-0.6.8a1.dist-info → reflex-0.7.0a1.dist-info}/METADATA +7 -10
  150. {reflex-0.6.8a1.dist-info → reflex-0.7.0a1.dist-info}/RECORD +153 -150
  151. {reflex-0.6.8a1.dist-info → reflex-0.7.0a1.dist-info}/WHEEL +1 -1
  152. reflex/experimental/assets.py +0 -37
  153. reflex/proxy.py +0 -119
  154. {reflex-0.6.8a1.dist-info → reflex-0.7.0a1.dist-info}/LICENSE +0 -0
  155. {reflex-0.6.8a1.dist-info → reflex-0.7.0a1.dist-info}/entry_points.txt +0 -0
reflex/event.py CHANGED
@@ -4,7 +4,6 @@ from __future__ import annotations
4
4
 
5
5
  import dataclasses
6
6
  import inspect
7
- import sys
8
7
  import types
9
8
  import urllib.parse
10
9
  from base64 import b64encode
@@ -25,7 +24,6 @@ from typing import (
25
24
  overload,
26
25
  )
27
26
 
28
- import typing_extensions
29
27
  from typing_extensions import (
30
28
  Concatenate,
31
29
  ParamSpec,
@@ -38,9 +36,14 @@ from typing_extensions import (
38
36
  )
39
37
 
40
38
  from reflex import constants
39
+ from reflex.constants.compiler import CompileVars, Hooks, Imports
41
40
  from reflex.constants.state import FRONTEND_EVENT_STATE
42
41
  from reflex.utils import console, format
43
- from reflex.utils.exceptions import EventFnArgMismatch, EventHandlerArgTypeMismatch
42
+ from reflex.utils.exceptions import (
43
+ EventFnArgMismatchError,
44
+ EventHandlerArgTypeMismatchError,
45
+ MissingAnnotationError,
46
+ )
44
47
  from reflex.utils.types import ArgsSpec, GenericType, typehint_issubclass
45
48
  from reflex.vars import VarData
46
49
  from reflex.vars.base import LiteralVar, Var
@@ -91,33 +94,9 @@ class Event:
91
94
  return f"{self.token}_{substate}"
92
95
 
93
96
 
94
- BACKGROUND_TASK_MARKER = "_reflex_background_task"
95
-
96
-
97
- def background(fn, *, __internal_reflex_call: bool = False):
98
- """Decorator to mark event handler as running in the background.
99
-
100
- Args:
101
- fn: The function to decorate.
102
-
103
- Returns:
104
- The same function, but with a marker set.
105
-
97
+ _EVENT_FIELDS: set[str] = {f.name for f in dataclasses.fields(Event)}
106
98
 
107
- Raises:
108
- TypeError: If the function is not a coroutine function or async generator.
109
- """
110
- if not __internal_reflex_call:
111
- console.deprecate(
112
- "background-decorator",
113
- "Use `rx.event(background=True)` instead.",
114
- "0.6.5",
115
- "0.7.0",
116
- )
117
- if not inspect.iscoroutinefunction(fn) and not inspect.isasyncgenfunction(fn):
118
- raise TypeError("Background task must be async function or generator.")
119
- setattr(fn, BACKGROUND_TASK_MARKER, True)
120
- return fn
99
+ BACKGROUND_TASK_MARKER = "_reflex_background_task"
121
100
 
122
101
 
123
102
  @dataclasses.dataclass(
@@ -264,7 +243,7 @@ class EventHandler(EventActionsMixin):
264
243
  raise EventHandlerTypeError(
265
244
  f"Arguments to event handlers must be Vars or JSON-serializable. Got {arg} of type {type(arg)}."
266
245
  ) from e
267
- payload = tuple(zip(fn_args, values))
246
+ payload = tuple(zip(fn_args, values, strict=False))
268
247
 
269
248
  # Return the event spec.
270
249
  return EventSpec(
@@ -284,7 +263,7 @@ class EventSpec(EventActionsMixin):
284
263
  """
285
264
 
286
265
  # The event handler.
287
- handler: EventHandler = dataclasses.field(default=None) # type: ignore
266
+ handler: EventHandler = dataclasses.field(default=None) # pyright: ignore [reportAssignmentType]
288
267
 
289
268
  # The handler on the client to process event.
290
269
  client_handler_name: str = dataclasses.field(default="")
@@ -353,12 +332,12 @@ class EventSpec(EventActionsMixin):
353
332
  arg = None
354
333
  try:
355
334
  for arg in args:
356
- values.append(LiteralVar.create(value=arg)) # noqa: PERF401
335
+ values.append(LiteralVar.create(value=arg)) # noqa: PERF401, RUF100
357
336
  except TypeError as e:
358
337
  raise EventHandlerTypeError(
359
338
  f"Arguments to event handlers must be Vars or JSON-serializable. Got {arg} of type {type(arg)}."
360
339
  ) from e
361
- new_payload = tuple(zip(fn_args, values))
340
+ new_payload = tuple(zip(fn_args, values, strict=False))
362
341
  return self.with_args(self.args + new_payload)
363
342
 
364
343
 
@@ -437,6 +416,7 @@ class EventChain(EventActionsMixin):
437
416
  value: EventType,
438
417
  args_spec: ArgsSpec | Sequence[ArgsSpec],
439
418
  key: Optional[str] = None,
419
+ **event_chain_kwargs,
440
420
  ) -> Union[EventChain, Var]:
441
421
  """Create an event chain from a variety of input types.
442
422
 
@@ -444,6 +424,7 @@ class EventChain(EventActionsMixin):
444
424
  value: The value to create the event chain from.
445
425
  args_spec: The args_spec of the event trigger being bound.
446
426
  key: The key of the event trigger being bound.
427
+ **event_chain_kwargs: Additional kwargs to pass to the EventChain constructor.
447
428
 
448
429
  Returns:
449
430
  The event chain.
@@ -462,6 +443,7 @@ class EventChain(EventActionsMixin):
462
443
  value=value.guess_type(),
463
444
  args_spec=args_spec,
464
445
  key=key,
446
+ **event_chain_kwargs,
465
447
  )
466
448
  else:
467
449
  raise ValueError(
@@ -501,7 +483,9 @@ class EventChain(EventActionsMixin):
501
483
  result = call_event_fn(value, args_spec, key=key)
502
484
  if isinstance(result, Var):
503
485
  # Recursively call this function if the lambda returned an EventChain Var.
504
- return cls.create(value=result, args_spec=args_spec, key=key)
486
+ return cls.create(
487
+ value=result, args_spec=args_spec, key=key, **event_chain_kwargs
488
+ )
505
489
  events = [*result]
506
490
 
507
491
  # Otherwise, raise an error.
@@ -518,7 +502,7 @@ class EventChain(EventActionsMixin):
518
502
  return cls(
519
503
  events=events,
520
504
  args_spec=args_spec,
521
- event_actions={},
505
+ **event_chain_kwargs,
522
506
  )
523
507
 
524
508
 
@@ -550,13 +534,13 @@ class JavasciptKeyboardEvent:
550
534
  """Interface for a Javascript KeyboardEvent https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent."""
551
535
 
552
536
  key: str = ""
553
- altKey: bool = False
554
- ctrlKey: bool = False
555
- metaKey: bool = False
556
- shiftKey: bool = False
537
+ altKey: bool = False # noqa: N815
538
+ ctrlKey: bool = False # noqa: N815
539
+ metaKey: bool = False # noqa: N815
540
+ shiftKey: bool = False # noqa: N815
557
541
 
558
542
 
559
- def input_event(e: Var[JavascriptInputEvent]) -> Tuple[Var[str]]:
543
+ def input_event(e: ObjectVar[JavascriptInputEvent]) -> Tuple[Var[str]]:
560
544
  """Get the value from an input event.
561
545
 
562
546
  Args:
@@ -577,7 +561,9 @@ class KeyInputInfo(TypedDict):
577
561
  shift_key: bool
578
562
 
579
563
 
580
- def key_event(e: Var[JavasciptKeyboardEvent]) -> Tuple[Var[str], Var[KeyInputInfo]]:
564
+ def key_event(
565
+ e: ObjectVar[JavasciptKeyboardEvent],
566
+ ) -> Tuple[Var[str], Var[KeyInputInfo]]:
581
567
  """Get the key from a keyboard event.
582
568
 
583
569
  Args:
@@ -587,7 +573,7 @@ def key_event(e: Var[JavasciptKeyboardEvent]) -> Tuple[Var[str], Var[KeyInputInf
587
573
  The key from the keyboard event.
588
574
  """
589
575
  return (
590
- e.key,
576
+ e.key.to(str),
591
577
  Var.create(
592
578
  {
593
579
  "alt_key": e.altKey,
@@ -595,7 +581,7 @@ def key_event(e: Var[JavasciptKeyboardEvent]) -> Tuple[Var[str], Var[KeyInputInf
595
581
  "meta_key": e.metaKey,
596
582
  "shift_key": e.shiftKey,
597
583
  },
598
- ),
584
+ ).to(KeyInputInfo),
599
585
  )
600
586
 
601
587
 
@@ -605,7 +591,7 @@ def no_args_event_spec() -> Tuple[()]:
605
591
  Returns:
606
592
  An empty tuple.
607
593
  """
608
- return () # type: ignore
594
+ return ()
609
595
 
610
596
 
611
597
  # These chains can be used for their side effects when no other events are desired.
@@ -633,9 +619,9 @@ class IdentityEventReturn(Generic[T], Protocol):
633
619
 
634
620
 
635
621
  @overload
636
- def passthrough_event_spec(
622
+ def passthrough_event_spec( # pyright: ignore [reportOverlappingOverload]
637
623
  event_type: Type[T], /
638
- ) -> Callable[[Var[T]], Tuple[Var[T]]]: ... # type: ignore
624
+ ) -> Callable[[Var[T]], Tuple[Var[T]]]: ...
639
625
 
640
626
 
641
627
  @overload
@@ -648,7 +634,7 @@ def passthrough_event_spec(
648
634
  def passthrough_event_spec(*event_types: Type[T]) -> IdentityEventReturn[T]: ...
649
635
 
650
636
 
651
- def passthrough_event_spec(*event_types: Type[T]) -> IdentityEventReturn[T]: # type: ignore
637
+ def passthrough_event_spec(*event_types: Type[T]) -> IdentityEventReturn[T]: # pyright: ignore [reportInconsistentOverload]
652
638
  """A helper function that returns the input event as output.
653
639
 
654
640
  Args:
@@ -662,9 +648,9 @@ def passthrough_event_spec(*event_types: Type[T]) -> IdentityEventReturn[T]: #
662
648
  return values
663
649
 
664
650
  inner_type = tuple(Var[event_type] for event_type in event_types)
665
- return_annotation = Tuple[inner_type] # type: ignore
651
+ return_annotation = Tuple[inner_type]
666
652
 
667
- inner.__signature__ = inspect.signature(inner).replace( # type: ignore
653
+ inner.__signature__ = inspect.signature(inner).replace( # pyright: ignore [reportFunctionMemberAccess]
668
654
  parameters=[
669
655
  inspect.Parameter(
670
656
  f"ev_{i}",
@@ -746,7 +732,7 @@ class FileUpload:
746
732
  # Call the lambda to get the event chain.
747
733
  events = call_event_fn(
748
734
  on_upload_progress, self.on_upload_progress_args_spec
749
- ) # type: ignore
735
+ )
750
736
  else:
751
737
  raise ValueError(f"{on_upload_progress} is not a valid event handler.")
752
738
  if isinstance(events, Var):
@@ -793,7 +779,7 @@ def server_side(name: str, sig: inspect.Signature, **kwargs) -> EventSpec:
793
779
  return None
794
780
 
795
781
  fn.__qualname__ = name
796
- fn.__signature__ = sig
782
+ fn.__signature__ = sig # pyright: ignore [reportFunctionMemberAccess]
797
783
  return EventSpec(
798
784
  handler=EventHandler(fn=fn, state_full_name=FRONTEND_EVENT_STATE),
799
785
  args=tuple(
@@ -806,29 +792,10 @@ def server_side(name: str, sig: inspect.Signature, **kwargs) -> EventSpec:
806
792
  )
807
793
 
808
794
 
809
- @overload
810
- def redirect(
811
- path: str | Var[str],
812
- is_external: Optional[bool] = None,
813
- replace: bool = False,
814
- ) -> EventSpec: ...
815
-
816
-
817
- @overload
818
- @typing_extensions.deprecated("`external` is deprecated use `is_external` instead")
819
- def redirect(
820
- path: str | Var[str],
821
- is_external: Optional[bool] = None,
822
- replace: bool = False,
823
- external: Optional[bool] = None,
824
- ) -> EventSpec: ...
825
-
826
-
827
795
  def redirect(
828
796
  path: str | Var[str],
829
- is_external: Optional[bool] = None,
797
+ is_external: bool = False,
830
798
  replace: bool = False,
831
- external: Optional[bool] = None,
832
799
  ) -> EventSpec:
833
800
  """Redirect to a new path.
834
801
 
@@ -836,26 +803,10 @@ def redirect(
836
803
  path: The path to redirect to.
837
804
  is_external: Whether to open in new tab or not.
838
805
  replace: If True, the current page will not create a new history entry.
839
- external(Deprecated): Whether to open in new tab or not.
840
806
 
841
807
  Returns:
842
808
  An event to redirect to the path.
843
809
  """
844
- if external is not None:
845
- console.deprecate(
846
- "The `external` prop in `rx.redirect`",
847
- "use `is_external` instead.",
848
- "0.6.6",
849
- "0.7.0",
850
- )
851
-
852
- # is_external should take precedence over external.
853
- is_external = (
854
- (False if external is None else external)
855
- if is_external is None
856
- else is_external
857
- )
858
-
859
810
  return server_side(
860
811
  "_redirect",
861
812
  get_fn_signature(redirect),
@@ -1101,13 +1052,13 @@ def download(
1101
1052
 
1102
1053
  is_data_url = (data.js_type() == "string") & (
1103
1054
  data.to(str).startswith("data:")
1104
- ) # type: ignore
1055
+ )
1105
1056
 
1106
1057
  # If it's a data: URI, use it as is, otherwise convert the Var to JSON in a data: URI.
1107
- url = cond( # type: ignore
1058
+ url = cond(
1108
1059
  is_data_url,
1109
1060
  data.to(str),
1110
- "data:text/plain," + data.to_string(), # type: ignore
1061
+ "data:text/plain," + data.to_string(),
1111
1062
  )
1112
1063
  elif isinstance(data, bytes):
1113
1064
  # Caller provided bytes, so base64 encode it as a data: URI.
@@ -1126,7 +1077,8 @@ def download(
1126
1077
  )
1127
1078
 
1128
1079
 
1129
- def _callback_arg_spec(eval_result):
1080
+ # This function seems unused. Check if we still need it. If not, remove in 0.7.0
1081
+ def _callback_arg_spec(eval_result: Any):
1130
1082
  """ArgSpec for call_script callback function.
1131
1083
 
1132
1084
  Args:
@@ -1231,7 +1183,7 @@ def run_script(
1231
1183
  return call_function(ArgsFunctionOperation.create((), javascript_code), callback)
1232
1184
 
1233
1185
 
1234
- def get_event(state, event):
1186
+ def get_event(state: BaseState, event: str):
1235
1187
  """Get the event from the given state.
1236
1188
 
1237
1189
  Args:
@@ -1244,7 +1196,7 @@ def get_event(state, event):
1244
1196
  return f"{state.get_name()}.{event}"
1245
1197
 
1246
1198
 
1247
- def get_hydrate_event(state) -> str:
1199
+ def get_hydrate_event(state: BaseState) -> str:
1248
1200
  """Get the name of the hydrate event for the state.
1249
1201
 
1250
1202
  Args:
@@ -1272,13 +1224,16 @@ def call_event_handler(
1272
1224
  event_spec: The lambda that define the argument(s) to pass to the event handler.
1273
1225
  key: The key to pass to the event handler.
1274
1226
 
1227
+ Raises:
1228
+ EventHandlerArgTypeMismatchError: If the event handler arguments do not match the event spec. #noqa: DAR402
1229
+ TypeError: If the event handler arguments are invalid.
1230
+
1275
1231
  Returns:
1276
1232
  The event spec from calling the event handler.
1277
1233
 
1278
- # noqa: DAR401 failure
1279
-
1234
+ #noqa: DAR401
1280
1235
  """
1281
- event_spec_args = parse_args_spec(event_spec) # type: ignore
1236
+ event_spec_args = parse_args_spec(event_spec)
1282
1237
 
1283
1238
  if isinstance(event_callback, EventSpec):
1284
1239
  check_fn_match_arg_spec(
@@ -1313,10 +1268,15 @@ def call_event_handler(
1313
1268
  ),
1314
1269
  )
1315
1270
  )
1271
+ type_match_found: dict[str, bool] = {}
1272
+ delayed_exceptions: list[EventHandlerArgTypeMismatchError] = []
1316
1273
 
1317
- if event_spec_return_types:
1318
- failures = []
1274
+ try:
1275
+ type_hints_of_provided_callback = get_type_hints(event_callback.fn)
1276
+ except NameError:
1277
+ type_hints_of_provided_callback = {}
1319
1278
 
1279
+ if event_spec_return_types:
1320
1280
  event_callback_spec = inspect.getfullargspec(event_callback.fn)
1321
1281
 
1322
1282
  for event_spec_index, event_spec_return_type in enumerate(
@@ -1328,43 +1288,35 @@ def call_event_handler(
1328
1288
  arg if get_origin(arg) is not Var else get_args(arg)[0] for arg in args
1329
1289
  ]
1330
1290
 
1331
- try:
1332
- type_hints_of_provided_callback = get_type_hints(event_callback.fn)
1333
- except NameError:
1334
- type_hints_of_provided_callback = {}
1335
-
1336
- failed_type_check = False
1337
-
1338
1291
  # check that args of event handler are matching the spec if type hints are provided
1339
1292
  for i, arg in enumerate(event_callback_spec.args[1:]):
1340
1293
  if arg not in type_hints_of_provided_callback:
1341
1294
  continue
1342
1295
 
1296
+ type_match_found.setdefault(arg, False)
1297
+
1343
1298
  try:
1344
1299
  compare_result = typehint_issubclass(
1345
1300
  args_types_without_vars[i], type_hints_of_provided_callback[arg]
1346
1301
  )
1347
- except TypeError:
1348
- # TODO: In 0.7.0, remove this block and raise the exception
1349
- # raise TypeError(
1350
- # 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
1351
- # ) from e
1352
- console.warn(
1302
+ except TypeError as te:
1303
+ raise TypeError(
1353
1304
  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}."
1354
- )
1355
- compare_result = False
1305
+ ) from te
1356
1306
 
1357
1307
  if compare_result:
1308
+ type_match_found[arg] = True
1358
1309
  continue
1359
1310
  else:
1360
- failure = EventHandlerArgTypeMismatch(
1361
- 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."
1311
+ type_match_found[arg] = False
1312
+ delayed_exceptions.append(
1313
+ EventHandlerArgTypeMismatchError(
1314
+ 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."
1315
+ )
1362
1316
  )
1363
- failures.append(failure)
1364
- failed_type_check = True
1365
- break
1366
1317
 
1367
- if not failed_type_check:
1318
+ if all(type_match_found.values()):
1319
+ delayed_exceptions.clear()
1368
1320
  if event_spec_index:
1369
1321
  args = get_args(event_spec_return_types[0])
1370
1322
 
@@ -1386,17 +1338,12 @@ def call_event_handler(
1386
1338
  f"Event handler {key} expects ({expect_string}) -> () but got ({given_string}) -> () as annotated in {event_callback.fn.__qualname__} instead. "
1387
1339
  f"This may lead to unexpected behavior but is intentionally ignored for {key}."
1388
1340
  )
1389
- return event_callback(*event_spec_args)
1390
-
1391
- if failures:
1392
- console.deprecate(
1393
- "Mismatched event handler argument types",
1394
- "\n".join([str(f) for f in failures]),
1395
- "0.6.5",
1396
- "0.7.0",
1397
- )
1341
+ break
1398
1342
 
1399
- return event_callback(*event_spec_args) # type: ignore
1343
+ if delayed_exceptions:
1344
+ raise delayed_exceptions[0]
1345
+
1346
+ return event_callback(*event_spec_args)
1400
1347
 
1401
1348
 
1402
1349
  def unwrap_var_annotation(annotation: GenericType):
@@ -1408,31 +1355,31 @@ def unwrap_var_annotation(annotation: GenericType):
1408
1355
  Returns:
1409
1356
  The unwrapped annotation.
1410
1357
  """
1411
- if get_origin(annotation) is Var and (args := get_args(annotation)):
1358
+ if get_origin(annotation) in (Var, ObjectVar) and (args := get_args(annotation)):
1412
1359
  return args[0]
1413
1360
  return annotation
1414
1361
 
1415
1362
 
1416
- def resolve_annotation(annotations: dict[str, Any], arg_name: str):
1363
+ def resolve_annotation(annotations: dict[str, Any], arg_name: str, spec: ArgsSpec):
1417
1364
  """Resolve the annotation for the given argument name.
1418
1365
 
1419
1366
  Args:
1420
1367
  annotations: The annotations.
1421
1368
  arg_name: The argument name.
1369
+ spec: The specs which the annotations come from.
1370
+
1371
+ Raises:
1372
+ MissingAnnotationError: If the annotation is missing for non-lambda methods.
1422
1373
 
1423
1374
  Returns:
1424
1375
  The resolved annotation.
1425
1376
  """
1426
1377
  annotation = annotations.get(arg_name)
1427
1378
  if annotation is None:
1428
- console.deprecate(
1429
- feature_name="Unannotated event handler arguments",
1430
- reason="Provide type annotations for event handler arguments.",
1431
- deprecation_version="0.6.3",
1432
- removal_version="0.7.0",
1433
- )
1434
- # Allow arbitrary attribute access two levels deep until removed.
1435
- return Dict[str, dict]
1379
+ if not isinstance(spec, types.LambdaType):
1380
+ raise MissingAnnotationError(var_name=arg_name)
1381
+ else:
1382
+ return dict[str, dict]
1436
1383
  return annotation
1437
1384
 
1438
1385
 
@@ -1454,7 +1401,13 @@ def parse_args_spec(arg_spec: ArgsSpec | Sequence[ArgsSpec]):
1454
1401
  arg_spec(
1455
1402
  *[
1456
1403
  Var(f"_{l_arg}").to(
1457
- unwrap_var_annotation(resolve_annotation(annotations, l_arg))
1404
+ unwrap_var_annotation(
1405
+ resolve_annotation(
1406
+ annotations,
1407
+ l_arg,
1408
+ spec=arg_spec,
1409
+ )
1410
+ )
1458
1411
  )
1459
1412
  for l_arg in spec.args
1460
1413
  ]
@@ -1470,7 +1423,7 @@ def check_fn_match_arg_spec(
1470
1423
  func_name: str | None = None,
1471
1424
  ):
1472
1425
  """Ensures that the function signature matches the passed argument specification
1473
- or raises an EventFnArgMismatch if they do not.
1426
+ or raises an EventFnArgMismatchError if they do not.
1474
1427
 
1475
1428
  Args:
1476
1429
  user_func: The function to be validated.
@@ -1480,7 +1433,7 @@ def check_fn_match_arg_spec(
1480
1433
  func_name: The name of the function to be validated.
1481
1434
 
1482
1435
  Raises:
1483
- EventFnArgMismatch: Raised if the number of mandatory arguments do not match
1436
+ EventFnArgMismatchError: Raised if the number of mandatory arguments do not match
1484
1437
  """
1485
1438
  user_args = inspect.getfullargspec(user_func).args
1486
1439
  # Drop the first argument if it's a bound method
@@ -1496,7 +1449,7 @@ def check_fn_match_arg_spec(
1496
1449
  number_of_event_args = len(parsed_event_args)
1497
1450
 
1498
1451
  if number_of_user_args - number_of_user_default_args > number_of_event_args:
1499
- raise EventFnArgMismatch(
1452
+ raise EventFnArgMismatchError(
1500
1453
  f"Event {key} only provides {number_of_event_args} arguments, but "
1501
1454
  f"{func_name or user_func} requires at least {number_of_user_args - number_of_user_default_args} "
1502
1455
  "arguments to be passed to the event handler.\n"
@@ -1584,7 +1537,7 @@ def get_handler_args(
1584
1537
 
1585
1538
 
1586
1539
  def fix_events(
1587
- events: list[EventHandler | EventSpec] | None,
1540
+ events: list[EventSpec | EventHandler] | None,
1588
1541
  token: str,
1589
1542
  router_data: dict[str, Any] | None = None,
1590
1543
  ) -> list[Event]:
@@ -1624,7 +1577,7 @@ def fix_events(
1624
1577
  if not isinstance(e, EventSpec):
1625
1578
  raise ValueError(f"Unexpected event type, {type(e)}.")
1626
1579
  name = format.format_event_handler(e.handler)
1627
- payload = {k._js_expr: v._decode() for k, v in e.args} # type: ignore
1580
+ payload = {k._js_expr: v._decode() for k, v in e.args}
1628
1581
 
1629
1582
  # Filter router_data to reduce payload size
1630
1583
  event_router_data = {
@@ -1668,12 +1621,12 @@ class EventVar(ObjectVar, python_types=EventSpec):
1668
1621
  @dataclasses.dataclass(
1669
1622
  eq=False,
1670
1623
  frozen=True,
1671
- **{"slots": True} if sys.version_info >= (3, 10) else {},
1624
+ slots=True,
1672
1625
  )
1673
1626
  class LiteralEventVar(VarOperationCall, LiteralVar, EventVar):
1674
1627
  """A literal event var."""
1675
1628
 
1676
- _var_value: EventSpec = dataclasses.field(default=None) # type: ignore
1629
+ _var_value: EventSpec = dataclasses.field(default=None) # pyright: ignore [reportAssignmentType]
1677
1630
 
1678
1631
  def __hash__(self) -> int:
1679
1632
  """Get the hash of the var.
@@ -1729,7 +1682,7 @@ class EventChainVar(BuilderFunctionVar, python_types=EventChain):
1729
1682
  @dataclasses.dataclass(
1730
1683
  eq=False,
1731
1684
  frozen=True,
1732
- **{"slots": True} if sys.version_info >= (3, 10) else {},
1685
+ slots=True,
1733
1686
  )
1734
1687
  # Note: LiteralVar is second in the inheritance list allowing it act like a
1735
1688
  # CachedVarOperation (ArgsFunctionOperation) and get the _js_expr from the
@@ -1737,7 +1690,7 @@ class EventChainVar(BuilderFunctionVar, python_types=EventChain):
1737
1690
  class LiteralEventChainVar(ArgsFunctionOperationBuilder, LiteralVar, EventChainVar):
1738
1691
  """A literal event chain var."""
1739
1692
 
1740
- _var_value: EventChain = dataclasses.field(default=None) # type: ignore
1693
+ _var_value: EventChain = dataclasses.field(default=None) # pyright: ignore [reportAssignmentType]
1741
1694
 
1742
1695
  def __hash__(self) -> int:
1743
1696
  """Get the hash of the var.
@@ -1761,13 +1714,16 @@ class LiteralEventChainVar(ArgsFunctionOperationBuilder, LiteralVar, EventChainV
1761
1714
 
1762
1715
  Returns:
1763
1716
  The created LiteralEventChainVar instance.
1717
+
1718
+ Raises:
1719
+ ValueError: If the invocation is not a FunctionVar.
1764
1720
  """
1765
1721
  arg_spec = (
1766
1722
  value.args_spec[0]
1767
1723
  if isinstance(value.args_spec, Sequence)
1768
1724
  else value.args_spec
1769
1725
  )
1770
- sig = inspect.signature(arg_spec) # type: ignore
1726
+ sig = inspect.signature(arg_spec) # pyright: ignore [reportArgumentType]
1771
1727
  if sig.parameters:
1772
1728
  arg_def = tuple((f"_{p}" for p in sig.parameters))
1773
1729
  arg_def_expr = LiteralVar.create([Var(_js_expr=arg) for arg in arg_def])
@@ -1778,10 +1734,21 @@ class LiteralEventChainVar(ArgsFunctionOperationBuilder, LiteralVar, EventChainV
1778
1734
  arg_def_expr = Var(_js_expr="args")
1779
1735
 
1780
1736
  if value.invocation is None:
1781
- invocation = FunctionStringVar.create("addEvents")
1737
+ invocation = FunctionStringVar.create(
1738
+ CompileVars.ADD_EVENTS,
1739
+ _var_data=VarData(
1740
+ imports=Imports.EVENTS,
1741
+ hooks={Hooks.EVENTS: None},
1742
+ ),
1743
+ )
1782
1744
  else:
1783
1745
  invocation = value.invocation
1784
1746
 
1747
+ if invocation is not None and not isinstance(invocation, FunctionVar):
1748
+ raise ValueError(
1749
+ f"EventChain invocation must be a FunctionVar, got {invocation!s} of type {invocation._var_type!s}."
1750
+ )
1751
+
1785
1752
  return cls(
1786
1753
  _js_expr="",
1787
1754
  _var_type=EventChain,
@@ -1805,8 +1772,6 @@ V3 = TypeVar("V3")
1805
1772
  V4 = TypeVar("V4")
1806
1773
  V5 = TypeVar("V5")
1807
1774
 
1808
- background_event_decorator = background
1809
-
1810
1775
 
1811
1776
  class EventCallback(Generic[P, T]):
1812
1777
  """A descriptor that wraps a function to be used as an event."""
@@ -1871,7 +1836,7 @@ class EventCallback(Generic[P, T]):
1871
1836
  value4: V4 | Var[V4],
1872
1837
  ) -> EventCallback[Q, T]: ...
1873
1838
 
1874
- def __call__(self, *values) -> EventCallback: # type: ignore
1839
+ def __call__(self, *values) -> EventCallback: # pyright: ignore [reportInconsistentOverload]
1875
1840
  """Call the function with the values.
1876
1841
 
1877
1842
  Args:
@@ -1880,17 +1845,17 @@ class EventCallback(Generic[P, T]):
1880
1845
  Returns:
1881
1846
  The function with the values.
1882
1847
  """
1883
- return self.func(*values) # type: ignore
1848
+ return self.func(*values) # pyright: ignore [reportCallIssue, reportReturnType]
1884
1849
 
1885
1850
  @overload
1886
1851
  def __get__(
1887
- self: EventCallback[P, T], instance: None, owner
1852
+ self: EventCallback[P, T], instance: None, owner: Any
1888
1853
  ) -> EventCallback[P, T]: ...
1889
1854
 
1890
1855
  @overload
1891
- def __get__(self, instance, owner) -> Callable[P, T]: ...
1856
+ def __get__(self, instance: Any, owner: Any) -> Callable[P, T]: ...
1892
1857
 
1893
- def __get__(self, instance, owner) -> Callable: # type: ignore
1858
+ def __get__(self, instance: Any, owner: Any) -> Callable:
1894
1859
  """Get the function with the instance bound to it.
1895
1860
 
1896
1861
  Args:
@@ -1901,9 +1866,9 @@ class EventCallback(Generic[P, T]):
1901
1866
  The function with the instance bound to it
1902
1867
  """
1903
1868
  if instance is None:
1904
- return self.func # type: ignore
1869
+ return self.func
1905
1870
 
1906
- return partial(self.func, instance) # type: ignore
1871
+ return partial(self.func, instance)
1907
1872
 
1908
1873
 
1909
1874
  G = ParamSpec("G")
@@ -1954,7 +1919,7 @@ class EventNamespace(types.SimpleNamespace):
1954
1919
  @staticmethod
1955
1920
  def __call__(
1956
1921
  func: None = None, *, background: bool | None = None
1957
- ) -> Callable[[Callable[Concatenate[BASE_STATE, P], T]], EventCallback[P, T]]: ...
1922
+ ) -> Callable[[Callable[Concatenate[BASE_STATE, P], T]], EventCallback[P, T]]: ... # pyright: ignore [reportInvalidTypeVarUse]
1958
1923
 
1959
1924
  @overload
1960
1925
  @staticmethod
@@ -1979,6 +1944,9 @@ class EventNamespace(types.SimpleNamespace):
1979
1944
  func: The function to wrap.
1980
1945
  background: Whether the event should be run in the background. Defaults to False.
1981
1946
 
1947
+ Raises:
1948
+ TypeError: If background is True and the function is not a coroutine or async generator. # noqa: DAR402
1949
+
1982
1950
  Returns:
1983
1951
  The wrapped function.
1984
1952
  """
@@ -1987,8 +1955,14 @@ class EventNamespace(types.SimpleNamespace):
1987
1955
  func: Callable[Concatenate[BASE_STATE, P], T],
1988
1956
  ) -> EventCallback[P, T]:
1989
1957
  if background is True:
1990
- return background_event_decorator(func, __internal_reflex_call=True) # type: ignore
1991
- return func # type: ignore
1958
+ if not inspect.iscoroutinefunction(
1959
+ func
1960
+ ) and not inspect.isasyncgenfunction(func):
1961
+ raise TypeError(
1962
+ "Background task must be async function or generator."
1963
+ )
1964
+ setattr(func, BACKGROUND_TASK_MARKER, True)
1965
+ return func # pyright: ignore [reportReturnType]
1992
1966
 
1993
1967
  if func is not None:
1994
1968
  return wrapper(func)