reflex 0.6.8a2__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.
- reflex/.templates/jinja/custom_components/pyproject.toml.jinja2 +1 -1
- reflex/.templates/jinja/web/pages/_app.js.jinja2 +7 -7
- reflex/.templates/jinja/web/pages/utils.js.jinja2 +2 -2
- reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js +1 -4
- reflex/.templates/web/utils/state.js +65 -36
- reflex/__init__.py +4 -17
- reflex/__init__.pyi +1 -2
- reflex/app.py +244 -109
- reflex/app_mixins/lifespan.py +9 -9
- reflex/app_mixins/middleware.py +6 -6
- reflex/app_module_for_backend.py +3 -7
- reflex/base.py +7 -7
- reflex/compiler/compiler.py +8 -0
- reflex/compiler/utils.py +35 -6
- reflex/components/base/bare.py +1 -1
- reflex/components/base/error_boundary.py +2 -1
- reflex/components/base/error_boundary.pyi +2 -1
- reflex/components/base/meta.py +2 -2
- reflex/components/base/strict_mode.py +10 -0
- reflex/components/base/strict_mode.pyi +57 -0
- reflex/components/component.py +38 -77
- reflex/components/core/banner.py +83 -4
- reflex/components/core/banner.pyi +86 -0
- reflex/components/core/breakpoints.py +3 -1
- reflex/components/core/client_side_routing.py +1 -1
- reflex/components/core/client_side_routing.pyi +1 -1
- reflex/components/core/cond.py +9 -10
- reflex/components/core/debounce.py +1 -1
- reflex/components/core/foreach.py +23 -3
- reflex/components/core/html.py +1 -1
- reflex/components/core/match.py +5 -5
- reflex/components/core/sticky.py +160 -0
- reflex/components/core/sticky.pyi +449 -0
- reflex/components/core/upload.py +2 -2
- reflex/components/datadisplay/code.py +5 -14
- reflex/components/datadisplay/dataeditor.py +7 -4
- reflex/components/datadisplay/logo.py +13 -8
- reflex/components/datadisplay/shiki_code_block.py +14 -9
- reflex/components/dynamic.py +22 -3
- reflex/components/el/constants/reflex.py +1 -1
- reflex/components/el/element.py +1 -1
- reflex/components/el/elements/forms.py +4 -4
- reflex/components/el/elements/forms.pyi +4 -4
- reflex/components/lucide/icon.py +46 -8
- reflex/components/lucide/icon.pyi +54 -0
- reflex/components/markdown/markdown.py +10 -8
- reflex/components/moment/moment.py +2 -2
- reflex/components/next/image.py +16 -4
- reflex/components/next/image.pyi +4 -2
- reflex/components/next/link.py +1 -1
- reflex/components/plotly/plotly.py +5 -5
- reflex/components/props.py +3 -3
- reflex/components/radix/__init__.pyi +1 -1
- reflex/components/radix/primitives/accordion.py +9 -5
- reflex/components/radix/primitives/accordion.pyi +3 -1
- reflex/components/radix/primitives/drawer.py +5 -2
- reflex/components/radix/primitives/drawer.pyi +4 -4
- reflex/components/radix/primitives/form.pyi +6 -6
- reflex/components/radix/primitives/progress.py +1 -1
- reflex/components/radix/primitives/slider.py +1 -1
- reflex/components/radix/themes/color_mode.py +11 -9
- reflex/components/radix/themes/components/alert_dialog.py +3 -0
- reflex/components/radix/themes/components/card.py +1 -1
- reflex/components/radix/themes/components/card.pyi +1 -1
- reflex/components/radix/themes/components/context_menu.py +5 -0
- reflex/components/radix/themes/components/dialog.py +3 -0
- reflex/components/radix/themes/components/dropdown_menu.py +5 -0
- reflex/components/radix/themes/components/hover_card.py +3 -0
- reflex/components/radix/themes/components/icon_button.py +2 -2
- reflex/components/radix/themes/components/icon_button.pyi +1 -0
- reflex/components/radix/themes/components/popover.py +3 -0
- reflex/components/radix/themes/components/radio_cards.py +2 -0
- reflex/components/radix/themes/components/radio_group.py +1 -1
- reflex/components/radix/themes/components/select.py +3 -0
- reflex/components/radix/themes/components/tabs.py +3 -0
- reflex/components/radix/themes/components/text_area.py +12 -0
- reflex/components/radix/themes/components/text_area.pyi +2 -0
- reflex/components/radix/themes/components/text_field.py +1 -1
- reflex/components/radix/themes/components/tooltip.py +3 -1
- reflex/components/radix/themes/components/tooltip.pyi +1 -0
- reflex/components/radix/themes/layout/__init__.pyi +1 -1
- reflex/components/radix/themes/layout/list.py +2 -2
- reflex/components/radix/themes/layout/stack.py +2 -2
- reflex/components/radix/themes/typography/link.py +1 -1
- reflex/components/radix/themes/typography/text.py +2 -2
- reflex/components/react_player/react_player.py +1 -1
- reflex/components/recharts/__init__.py +2 -0
- reflex/components/recharts/__init__.pyi +2 -0
- reflex/components/recharts/charts.py +15 -15
- reflex/components/recharts/general.py +19 -4
- reflex/components/recharts/general.pyi +55 -4
- reflex/components/recharts/polar.py +2 -2
- reflex/components/recharts/recharts.py +4 -4
- reflex/components/sonner/toast.py +15 -13
- reflex/components/sonner/toast.pyi +6 -6
- reflex/components/suneditor/editor.py +6 -4
- reflex/components/suneditor/editor.pyi +2 -2
- reflex/components/tags/iter_tag.py +3 -3
- reflex/components/tags/tag.py +25 -3
- reflex/config.py +48 -15
- reflex/constants/__init__.py +1 -0
- reflex/constants/base.py +4 -1
- reflex/constants/compiler.py +5 -2
- reflex/constants/config.py +8 -1
- reflex/constants/installer.py +9 -9
- reflex/constants/style.py +1 -1
- reflex/custom_components/custom_components.py +9 -7
- reflex/event.py +130 -161
- reflex/experimental/__init__.py +19 -11
- reflex/experimental/client_state.py +53 -28
- reflex/experimental/hooks.py +5 -5
- reflex/experimental/layout.py +8 -5
- reflex/experimental/layout.pyi +1 -1
- reflex/experimental/misc.py +3 -3
- reflex/istate/wrappers.py +1 -1
- reflex/middleware/hydrate_middleware.py +2 -2
- reflex/model.py +11 -6
- reflex/page.py +3 -3
- reflex/reflex.py +90 -19
- reflex/route.py +1 -1
- reflex/state.py +358 -401
- reflex/style.py +27 -3
- reflex/testing.py +29 -23
- reflex/utils/build.py +6 -2
- reflex/utils/codespaces.py +1 -4
- reflex/utils/compat.py +6 -5
- reflex/utils/console.py +52 -16
- reflex/utils/exceptions.py +76 -26
- reflex/utils/exec.py +69 -74
- reflex/utils/export.py +6 -1
- reflex/utils/format.py +7 -39
- reflex/utils/imports.py +2 -2
- reflex/utils/lazy_loader.py +7 -1
- reflex/utils/path_ops.py +28 -14
- reflex/utils/prerequisites.py +324 -65
- reflex/utils/processes.py +45 -32
- reflex/utils/pyi_generator.py +30 -25
- reflex/utils/registry.py +4 -4
- reflex/utils/serializers.py +1 -1
- reflex/utils/telemetry.py +5 -4
- reflex/utils/types.py +42 -18
- reflex/vars/base.py +650 -333
- reflex/vars/datetime.py +6 -7
- reflex/vars/dep_tracking.py +344 -0
- reflex/vars/function.py +11 -5
- reflex/vars/number.py +31 -43
- reflex/vars/object.py +63 -62
- reflex/vars/sequence.py +79 -67
- {reflex-0.6.8a2.dist-info → reflex-0.7.0a1.dist-info}/METADATA +7 -8
- {reflex-0.6.8a2.dist-info → reflex-0.7.0a1.dist-info}/RECORD +153 -149
- {reflex-0.6.8a2.dist-info → reflex-0.7.0a1.dist-info}/WHEEL +1 -1
- reflex/experimental/assets.py +0 -37
- {reflex-0.6.8a2.dist-info → reflex-0.7.0a1.dist-info}/LICENSE +0 -0
- {reflex-0.6.8a2.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
|
|
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
|
-
|
|
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
|
-
|
|
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) #
|
|
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
|
|
|
@@ -555,13 +534,13 @@ class JavasciptKeyboardEvent:
|
|
|
555
534
|
"""Interface for a Javascript KeyboardEvent https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent."""
|
|
556
535
|
|
|
557
536
|
key: str = ""
|
|
558
|
-
altKey: bool = False
|
|
559
|
-
ctrlKey: bool = False
|
|
560
|
-
metaKey: bool = False
|
|
561
|
-
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
|
|
562
541
|
|
|
563
542
|
|
|
564
|
-
def input_event(e:
|
|
543
|
+
def input_event(e: ObjectVar[JavascriptInputEvent]) -> Tuple[Var[str]]:
|
|
565
544
|
"""Get the value from an input event.
|
|
566
545
|
|
|
567
546
|
Args:
|
|
@@ -582,7 +561,9 @@ class KeyInputInfo(TypedDict):
|
|
|
582
561
|
shift_key: bool
|
|
583
562
|
|
|
584
563
|
|
|
585
|
-
def key_event(
|
|
564
|
+
def key_event(
|
|
565
|
+
e: ObjectVar[JavasciptKeyboardEvent],
|
|
566
|
+
) -> Tuple[Var[str], Var[KeyInputInfo]]:
|
|
586
567
|
"""Get the key from a keyboard event.
|
|
587
568
|
|
|
588
569
|
Args:
|
|
@@ -592,7 +573,7 @@ def key_event(e: Var[JavasciptKeyboardEvent]) -> Tuple[Var[str], Var[KeyInputInf
|
|
|
592
573
|
The key from the keyboard event.
|
|
593
574
|
"""
|
|
594
575
|
return (
|
|
595
|
-
e.key,
|
|
576
|
+
e.key.to(str),
|
|
596
577
|
Var.create(
|
|
597
578
|
{
|
|
598
579
|
"alt_key": e.altKey,
|
|
@@ -600,7 +581,7 @@ def key_event(e: Var[JavasciptKeyboardEvent]) -> Tuple[Var[str], Var[KeyInputInf
|
|
|
600
581
|
"meta_key": e.metaKey,
|
|
601
582
|
"shift_key": e.shiftKey,
|
|
602
583
|
},
|
|
603
|
-
),
|
|
584
|
+
).to(KeyInputInfo),
|
|
604
585
|
)
|
|
605
586
|
|
|
606
587
|
|
|
@@ -610,7 +591,7 @@ def no_args_event_spec() -> Tuple[()]:
|
|
|
610
591
|
Returns:
|
|
611
592
|
An empty tuple.
|
|
612
593
|
"""
|
|
613
|
-
return ()
|
|
594
|
+
return ()
|
|
614
595
|
|
|
615
596
|
|
|
616
597
|
# These chains can be used for their side effects when no other events are desired.
|
|
@@ -638,9 +619,9 @@ class IdentityEventReturn(Generic[T], Protocol):
|
|
|
638
619
|
|
|
639
620
|
|
|
640
621
|
@overload
|
|
641
|
-
def passthrough_event_spec(
|
|
622
|
+
def passthrough_event_spec( # pyright: ignore [reportOverlappingOverload]
|
|
642
623
|
event_type: Type[T], /
|
|
643
|
-
) -> Callable[[Var[T]], Tuple[Var[T]]]: ...
|
|
624
|
+
) -> Callable[[Var[T]], Tuple[Var[T]]]: ...
|
|
644
625
|
|
|
645
626
|
|
|
646
627
|
@overload
|
|
@@ -653,7 +634,7 @@ def passthrough_event_spec(
|
|
|
653
634
|
def passthrough_event_spec(*event_types: Type[T]) -> IdentityEventReturn[T]: ...
|
|
654
635
|
|
|
655
636
|
|
|
656
|
-
def passthrough_event_spec(*event_types: Type[T]) -> IdentityEventReturn[T]: #
|
|
637
|
+
def passthrough_event_spec(*event_types: Type[T]) -> IdentityEventReturn[T]: # pyright: ignore [reportInconsistentOverload]
|
|
657
638
|
"""A helper function that returns the input event as output.
|
|
658
639
|
|
|
659
640
|
Args:
|
|
@@ -667,9 +648,9 @@ def passthrough_event_spec(*event_types: Type[T]) -> IdentityEventReturn[T]: #
|
|
|
667
648
|
return values
|
|
668
649
|
|
|
669
650
|
inner_type = tuple(Var[event_type] for event_type in event_types)
|
|
670
|
-
return_annotation = Tuple[inner_type]
|
|
651
|
+
return_annotation = Tuple[inner_type]
|
|
671
652
|
|
|
672
|
-
inner.__signature__ = inspect.signature(inner).replace( #
|
|
653
|
+
inner.__signature__ = inspect.signature(inner).replace( # pyright: ignore [reportFunctionMemberAccess]
|
|
673
654
|
parameters=[
|
|
674
655
|
inspect.Parameter(
|
|
675
656
|
f"ev_{i}",
|
|
@@ -751,7 +732,7 @@ class FileUpload:
|
|
|
751
732
|
# Call the lambda to get the event chain.
|
|
752
733
|
events = call_event_fn(
|
|
753
734
|
on_upload_progress, self.on_upload_progress_args_spec
|
|
754
|
-
)
|
|
735
|
+
)
|
|
755
736
|
else:
|
|
756
737
|
raise ValueError(f"{on_upload_progress} is not a valid event handler.")
|
|
757
738
|
if isinstance(events, Var):
|
|
@@ -798,7 +779,7 @@ def server_side(name: str, sig: inspect.Signature, **kwargs) -> EventSpec:
|
|
|
798
779
|
return None
|
|
799
780
|
|
|
800
781
|
fn.__qualname__ = name
|
|
801
|
-
fn.__signature__ = sig
|
|
782
|
+
fn.__signature__ = sig # pyright: ignore [reportFunctionMemberAccess]
|
|
802
783
|
return EventSpec(
|
|
803
784
|
handler=EventHandler(fn=fn, state_full_name=FRONTEND_EVENT_STATE),
|
|
804
785
|
args=tuple(
|
|
@@ -811,29 +792,10 @@ def server_side(name: str, sig: inspect.Signature, **kwargs) -> EventSpec:
|
|
|
811
792
|
)
|
|
812
793
|
|
|
813
794
|
|
|
814
|
-
@overload
|
|
815
|
-
def redirect(
|
|
816
|
-
path: str | Var[str],
|
|
817
|
-
is_external: Optional[bool] = None,
|
|
818
|
-
replace: bool = False,
|
|
819
|
-
) -> EventSpec: ...
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
@overload
|
|
823
|
-
@typing_extensions.deprecated("`external` is deprecated use `is_external` instead")
|
|
824
|
-
def redirect(
|
|
825
|
-
path: str | Var[str],
|
|
826
|
-
is_external: Optional[bool] = None,
|
|
827
|
-
replace: bool = False,
|
|
828
|
-
external: Optional[bool] = None,
|
|
829
|
-
) -> EventSpec: ...
|
|
830
|
-
|
|
831
|
-
|
|
832
795
|
def redirect(
|
|
833
796
|
path: str | Var[str],
|
|
834
|
-
is_external:
|
|
797
|
+
is_external: bool = False,
|
|
835
798
|
replace: bool = False,
|
|
836
|
-
external: Optional[bool] = None,
|
|
837
799
|
) -> EventSpec:
|
|
838
800
|
"""Redirect to a new path.
|
|
839
801
|
|
|
@@ -841,26 +803,10 @@ def redirect(
|
|
|
841
803
|
path: The path to redirect to.
|
|
842
804
|
is_external: Whether to open in new tab or not.
|
|
843
805
|
replace: If True, the current page will not create a new history entry.
|
|
844
|
-
external(Deprecated): Whether to open in new tab or not.
|
|
845
806
|
|
|
846
807
|
Returns:
|
|
847
808
|
An event to redirect to the path.
|
|
848
809
|
"""
|
|
849
|
-
if external is not None:
|
|
850
|
-
console.deprecate(
|
|
851
|
-
"The `external` prop in `rx.redirect`",
|
|
852
|
-
"use `is_external` instead.",
|
|
853
|
-
"0.6.6",
|
|
854
|
-
"0.7.0",
|
|
855
|
-
)
|
|
856
|
-
|
|
857
|
-
# is_external should take precedence over external.
|
|
858
|
-
is_external = (
|
|
859
|
-
(False if external is None else external)
|
|
860
|
-
if is_external is None
|
|
861
|
-
else is_external
|
|
862
|
-
)
|
|
863
|
-
|
|
864
810
|
return server_side(
|
|
865
811
|
"_redirect",
|
|
866
812
|
get_fn_signature(redirect),
|
|
@@ -1106,13 +1052,13 @@ def download(
|
|
|
1106
1052
|
|
|
1107
1053
|
is_data_url = (data.js_type() == "string") & (
|
|
1108
1054
|
data.to(str).startswith("data:")
|
|
1109
|
-
)
|
|
1055
|
+
)
|
|
1110
1056
|
|
|
1111
1057
|
# If it's a data: URI, use it as is, otherwise convert the Var to JSON in a data: URI.
|
|
1112
|
-
url = cond(
|
|
1058
|
+
url = cond(
|
|
1113
1059
|
is_data_url,
|
|
1114
1060
|
data.to(str),
|
|
1115
|
-
"data:text/plain," + data.to_string(),
|
|
1061
|
+
"data:text/plain," + data.to_string(),
|
|
1116
1062
|
)
|
|
1117
1063
|
elif isinstance(data, bytes):
|
|
1118
1064
|
# Caller provided bytes, so base64 encode it as a data: URI.
|
|
@@ -1131,7 +1077,8 @@ def download(
|
|
|
1131
1077
|
)
|
|
1132
1078
|
|
|
1133
1079
|
|
|
1134
|
-
|
|
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):
|
|
1135
1082
|
"""ArgSpec for call_script callback function.
|
|
1136
1083
|
|
|
1137
1084
|
Args:
|
|
@@ -1236,7 +1183,7 @@ def run_script(
|
|
|
1236
1183
|
return call_function(ArgsFunctionOperation.create((), javascript_code), callback)
|
|
1237
1184
|
|
|
1238
1185
|
|
|
1239
|
-
def get_event(state, event):
|
|
1186
|
+
def get_event(state: BaseState, event: str):
|
|
1240
1187
|
"""Get the event from the given state.
|
|
1241
1188
|
|
|
1242
1189
|
Args:
|
|
@@ -1249,7 +1196,7 @@ def get_event(state, event):
|
|
|
1249
1196
|
return f"{state.get_name()}.{event}"
|
|
1250
1197
|
|
|
1251
1198
|
|
|
1252
|
-
def get_hydrate_event(state) -> str:
|
|
1199
|
+
def get_hydrate_event(state: BaseState) -> str:
|
|
1253
1200
|
"""Get the name of the hydrate event for the state.
|
|
1254
1201
|
|
|
1255
1202
|
Args:
|
|
@@ -1277,13 +1224,16 @@ def call_event_handler(
|
|
|
1277
1224
|
event_spec: The lambda that define the argument(s) to pass to the event handler.
|
|
1278
1225
|
key: The key to pass to the event handler.
|
|
1279
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
|
+
|
|
1280
1231
|
Returns:
|
|
1281
1232
|
The event spec from calling the event handler.
|
|
1282
1233
|
|
|
1283
|
-
#
|
|
1284
|
-
|
|
1234
|
+
#noqa: DAR401
|
|
1285
1235
|
"""
|
|
1286
|
-
event_spec_args = parse_args_spec(event_spec)
|
|
1236
|
+
event_spec_args = parse_args_spec(event_spec)
|
|
1287
1237
|
|
|
1288
1238
|
if isinstance(event_callback, EventSpec):
|
|
1289
1239
|
check_fn_match_arg_spec(
|
|
@@ -1318,10 +1268,15 @@ def call_event_handler(
|
|
|
1318
1268
|
),
|
|
1319
1269
|
)
|
|
1320
1270
|
)
|
|
1271
|
+
type_match_found: dict[str, bool] = {}
|
|
1272
|
+
delayed_exceptions: list[EventHandlerArgTypeMismatchError] = []
|
|
1321
1273
|
|
|
1322
|
-
|
|
1323
|
-
|
|
1274
|
+
try:
|
|
1275
|
+
type_hints_of_provided_callback = get_type_hints(event_callback.fn)
|
|
1276
|
+
except NameError:
|
|
1277
|
+
type_hints_of_provided_callback = {}
|
|
1324
1278
|
|
|
1279
|
+
if event_spec_return_types:
|
|
1325
1280
|
event_callback_spec = inspect.getfullargspec(event_callback.fn)
|
|
1326
1281
|
|
|
1327
1282
|
for event_spec_index, event_spec_return_type in enumerate(
|
|
@@ -1333,43 +1288,35 @@ def call_event_handler(
|
|
|
1333
1288
|
arg if get_origin(arg) is not Var else get_args(arg)[0] for arg in args
|
|
1334
1289
|
]
|
|
1335
1290
|
|
|
1336
|
-
try:
|
|
1337
|
-
type_hints_of_provided_callback = get_type_hints(event_callback.fn)
|
|
1338
|
-
except NameError:
|
|
1339
|
-
type_hints_of_provided_callback = {}
|
|
1340
|
-
|
|
1341
|
-
failed_type_check = False
|
|
1342
|
-
|
|
1343
1291
|
# check that args of event handler are matching the spec if type hints are provided
|
|
1344
1292
|
for i, arg in enumerate(event_callback_spec.args[1:]):
|
|
1345
1293
|
if arg not in type_hints_of_provided_callback:
|
|
1346
1294
|
continue
|
|
1347
1295
|
|
|
1296
|
+
type_match_found.setdefault(arg, False)
|
|
1297
|
+
|
|
1348
1298
|
try:
|
|
1349
1299
|
compare_result = typehint_issubclass(
|
|
1350
1300
|
args_types_without_vars[i], type_hints_of_provided_callback[arg]
|
|
1351
1301
|
)
|
|
1352
|
-
except TypeError:
|
|
1353
|
-
|
|
1354
|
-
# raise TypeError(
|
|
1355
|
-
# 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
|
|
1356
|
-
# ) from e
|
|
1357
|
-
console.warn(
|
|
1302
|
+
except TypeError as te:
|
|
1303
|
+
raise TypeError(
|
|
1358
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}."
|
|
1359
|
-
)
|
|
1360
|
-
compare_result = False
|
|
1305
|
+
) from te
|
|
1361
1306
|
|
|
1362
1307
|
if compare_result:
|
|
1308
|
+
type_match_found[arg] = True
|
|
1363
1309
|
continue
|
|
1364
1310
|
else:
|
|
1365
|
-
|
|
1366
|
-
|
|
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
|
+
)
|
|
1367
1316
|
)
|
|
1368
|
-
failures.append(failure)
|
|
1369
|
-
failed_type_check = True
|
|
1370
|
-
break
|
|
1371
1317
|
|
|
1372
|
-
if
|
|
1318
|
+
if all(type_match_found.values()):
|
|
1319
|
+
delayed_exceptions.clear()
|
|
1373
1320
|
if event_spec_index:
|
|
1374
1321
|
args = get_args(event_spec_return_types[0])
|
|
1375
1322
|
|
|
@@ -1391,17 +1338,12 @@ def call_event_handler(
|
|
|
1391
1338
|
f"Event handler {key} expects ({expect_string}) -> () but got ({given_string}) -> () as annotated in {event_callback.fn.__qualname__} instead. "
|
|
1392
1339
|
f"This may lead to unexpected behavior but is intentionally ignored for {key}."
|
|
1393
1340
|
)
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
if failures:
|
|
1397
|
-
console.deprecate(
|
|
1398
|
-
"Mismatched event handler argument types",
|
|
1399
|
-
"\n".join([str(f) for f in failures]),
|
|
1400
|
-
"0.6.5",
|
|
1401
|
-
"0.7.0",
|
|
1402
|
-
)
|
|
1341
|
+
break
|
|
1403
1342
|
|
|
1404
|
-
|
|
1343
|
+
if delayed_exceptions:
|
|
1344
|
+
raise delayed_exceptions[0]
|
|
1345
|
+
|
|
1346
|
+
return event_callback(*event_spec_args)
|
|
1405
1347
|
|
|
1406
1348
|
|
|
1407
1349
|
def unwrap_var_annotation(annotation: GenericType):
|
|
@@ -1413,31 +1355,31 @@ def unwrap_var_annotation(annotation: GenericType):
|
|
|
1413
1355
|
Returns:
|
|
1414
1356
|
The unwrapped annotation.
|
|
1415
1357
|
"""
|
|
1416
|
-
if get_origin(annotation)
|
|
1358
|
+
if get_origin(annotation) in (Var, ObjectVar) and (args := get_args(annotation)):
|
|
1417
1359
|
return args[0]
|
|
1418
1360
|
return annotation
|
|
1419
1361
|
|
|
1420
1362
|
|
|
1421
|
-
def resolve_annotation(annotations: dict[str, Any], arg_name: str):
|
|
1363
|
+
def resolve_annotation(annotations: dict[str, Any], arg_name: str, spec: ArgsSpec):
|
|
1422
1364
|
"""Resolve the annotation for the given argument name.
|
|
1423
1365
|
|
|
1424
1366
|
Args:
|
|
1425
1367
|
annotations: The annotations.
|
|
1426
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.
|
|
1427
1373
|
|
|
1428
1374
|
Returns:
|
|
1429
1375
|
The resolved annotation.
|
|
1430
1376
|
"""
|
|
1431
1377
|
annotation = annotations.get(arg_name)
|
|
1432
1378
|
if annotation is None:
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
removal_version="0.7.0",
|
|
1438
|
-
)
|
|
1439
|
-
# Allow arbitrary attribute access two levels deep until removed.
|
|
1440
|
-
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]
|
|
1441
1383
|
return annotation
|
|
1442
1384
|
|
|
1443
1385
|
|
|
@@ -1459,7 +1401,13 @@ def parse_args_spec(arg_spec: ArgsSpec | Sequence[ArgsSpec]):
|
|
|
1459
1401
|
arg_spec(
|
|
1460
1402
|
*[
|
|
1461
1403
|
Var(f"_{l_arg}").to(
|
|
1462
|
-
unwrap_var_annotation(
|
|
1404
|
+
unwrap_var_annotation(
|
|
1405
|
+
resolve_annotation(
|
|
1406
|
+
annotations,
|
|
1407
|
+
l_arg,
|
|
1408
|
+
spec=arg_spec,
|
|
1409
|
+
)
|
|
1410
|
+
)
|
|
1463
1411
|
)
|
|
1464
1412
|
for l_arg in spec.args
|
|
1465
1413
|
]
|
|
@@ -1475,7 +1423,7 @@ def check_fn_match_arg_spec(
|
|
|
1475
1423
|
func_name: str | None = None,
|
|
1476
1424
|
):
|
|
1477
1425
|
"""Ensures that the function signature matches the passed argument specification
|
|
1478
|
-
or raises an
|
|
1426
|
+
or raises an EventFnArgMismatchError if they do not.
|
|
1479
1427
|
|
|
1480
1428
|
Args:
|
|
1481
1429
|
user_func: The function to be validated.
|
|
@@ -1485,7 +1433,7 @@ def check_fn_match_arg_spec(
|
|
|
1485
1433
|
func_name: The name of the function to be validated.
|
|
1486
1434
|
|
|
1487
1435
|
Raises:
|
|
1488
|
-
|
|
1436
|
+
EventFnArgMismatchError: Raised if the number of mandatory arguments do not match
|
|
1489
1437
|
"""
|
|
1490
1438
|
user_args = inspect.getfullargspec(user_func).args
|
|
1491
1439
|
# Drop the first argument if it's a bound method
|
|
@@ -1501,7 +1449,7 @@ def check_fn_match_arg_spec(
|
|
|
1501
1449
|
number_of_event_args = len(parsed_event_args)
|
|
1502
1450
|
|
|
1503
1451
|
if number_of_user_args - number_of_user_default_args > number_of_event_args:
|
|
1504
|
-
raise
|
|
1452
|
+
raise EventFnArgMismatchError(
|
|
1505
1453
|
f"Event {key} only provides {number_of_event_args} arguments, but "
|
|
1506
1454
|
f"{func_name or user_func} requires at least {number_of_user_args - number_of_user_default_args} "
|
|
1507
1455
|
"arguments to be passed to the event handler.\n"
|
|
@@ -1589,7 +1537,7 @@ def get_handler_args(
|
|
|
1589
1537
|
|
|
1590
1538
|
|
|
1591
1539
|
def fix_events(
|
|
1592
|
-
events: list[
|
|
1540
|
+
events: list[EventSpec | EventHandler] | None,
|
|
1593
1541
|
token: str,
|
|
1594
1542
|
router_data: dict[str, Any] | None = None,
|
|
1595
1543
|
) -> list[Event]:
|
|
@@ -1629,7 +1577,7 @@ def fix_events(
|
|
|
1629
1577
|
if not isinstance(e, EventSpec):
|
|
1630
1578
|
raise ValueError(f"Unexpected event type, {type(e)}.")
|
|
1631
1579
|
name = format.format_event_handler(e.handler)
|
|
1632
|
-
payload = {k._js_expr: v._decode() for k, v in e.args}
|
|
1580
|
+
payload = {k._js_expr: v._decode() for k, v in e.args}
|
|
1633
1581
|
|
|
1634
1582
|
# Filter router_data to reduce payload size
|
|
1635
1583
|
event_router_data = {
|
|
@@ -1673,12 +1621,12 @@ class EventVar(ObjectVar, python_types=EventSpec):
|
|
|
1673
1621
|
@dataclasses.dataclass(
|
|
1674
1622
|
eq=False,
|
|
1675
1623
|
frozen=True,
|
|
1676
|
-
|
|
1624
|
+
slots=True,
|
|
1677
1625
|
)
|
|
1678
1626
|
class LiteralEventVar(VarOperationCall, LiteralVar, EventVar):
|
|
1679
1627
|
"""A literal event var."""
|
|
1680
1628
|
|
|
1681
|
-
_var_value: EventSpec = dataclasses.field(default=None) #
|
|
1629
|
+
_var_value: EventSpec = dataclasses.field(default=None) # pyright: ignore [reportAssignmentType]
|
|
1682
1630
|
|
|
1683
1631
|
def __hash__(self) -> int:
|
|
1684
1632
|
"""Get the hash of the var.
|
|
@@ -1734,7 +1682,7 @@ class EventChainVar(BuilderFunctionVar, python_types=EventChain):
|
|
|
1734
1682
|
@dataclasses.dataclass(
|
|
1735
1683
|
eq=False,
|
|
1736
1684
|
frozen=True,
|
|
1737
|
-
|
|
1685
|
+
slots=True,
|
|
1738
1686
|
)
|
|
1739
1687
|
# Note: LiteralVar is second in the inheritance list allowing it act like a
|
|
1740
1688
|
# CachedVarOperation (ArgsFunctionOperation) and get the _js_expr from the
|
|
@@ -1742,7 +1690,7 @@ class EventChainVar(BuilderFunctionVar, python_types=EventChain):
|
|
|
1742
1690
|
class LiteralEventChainVar(ArgsFunctionOperationBuilder, LiteralVar, EventChainVar):
|
|
1743
1691
|
"""A literal event chain var."""
|
|
1744
1692
|
|
|
1745
|
-
_var_value: EventChain = dataclasses.field(default=None) #
|
|
1693
|
+
_var_value: EventChain = dataclasses.field(default=None) # pyright: ignore [reportAssignmentType]
|
|
1746
1694
|
|
|
1747
1695
|
def __hash__(self) -> int:
|
|
1748
1696
|
"""Get the hash of the var.
|
|
@@ -1766,13 +1714,16 @@ class LiteralEventChainVar(ArgsFunctionOperationBuilder, LiteralVar, EventChainV
|
|
|
1766
1714
|
|
|
1767
1715
|
Returns:
|
|
1768
1716
|
The created LiteralEventChainVar instance.
|
|
1717
|
+
|
|
1718
|
+
Raises:
|
|
1719
|
+
ValueError: If the invocation is not a FunctionVar.
|
|
1769
1720
|
"""
|
|
1770
1721
|
arg_spec = (
|
|
1771
1722
|
value.args_spec[0]
|
|
1772
1723
|
if isinstance(value.args_spec, Sequence)
|
|
1773
1724
|
else value.args_spec
|
|
1774
1725
|
)
|
|
1775
|
-
sig = inspect.signature(arg_spec) #
|
|
1726
|
+
sig = inspect.signature(arg_spec) # pyright: ignore [reportArgumentType]
|
|
1776
1727
|
if sig.parameters:
|
|
1777
1728
|
arg_def = tuple((f"_{p}" for p in sig.parameters))
|
|
1778
1729
|
arg_def_expr = LiteralVar.create([Var(_js_expr=arg) for arg in arg_def])
|
|
@@ -1783,10 +1734,21 @@ class LiteralEventChainVar(ArgsFunctionOperationBuilder, LiteralVar, EventChainV
|
|
|
1783
1734
|
arg_def_expr = Var(_js_expr="args")
|
|
1784
1735
|
|
|
1785
1736
|
if value.invocation is None:
|
|
1786
|
-
invocation = FunctionStringVar.create(
|
|
1737
|
+
invocation = FunctionStringVar.create(
|
|
1738
|
+
CompileVars.ADD_EVENTS,
|
|
1739
|
+
_var_data=VarData(
|
|
1740
|
+
imports=Imports.EVENTS,
|
|
1741
|
+
hooks={Hooks.EVENTS: None},
|
|
1742
|
+
),
|
|
1743
|
+
)
|
|
1787
1744
|
else:
|
|
1788
1745
|
invocation = value.invocation
|
|
1789
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
|
+
|
|
1790
1752
|
return cls(
|
|
1791
1753
|
_js_expr="",
|
|
1792
1754
|
_var_type=EventChain,
|
|
@@ -1810,8 +1772,6 @@ V3 = TypeVar("V3")
|
|
|
1810
1772
|
V4 = TypeVar("V4")
|
|
1811
1773
|
V5 = TypeVar("V5")
|
|
1812
1774
|
|
|
1813
|
-
background_event_decorator = background
|
|
1814
|
-
|
|
1815
1775
|
|
|
1816
1776
|
class EventCallback(Generic[P, T]):
|
|
1817
1777
|
"""A descriptor that wraps a function to be used as an event."""
|
|
@@ -1876,7 +1836,7 @@ class EventCallback(Generic[P, T]):
|
|
|
1876
1836
|
value4: V4 | Var[V4],
|
|
1877
1837
|
) -> EventCallback[Q, T]: ...
|
|
1878
1838
|
|
|
1879
|
-
def __call__(self, *values) -> EventCallback: #
|
|
1839
|
+
def __call__(self, *values) -> EventCallback: # pyright: ignore [reportInconsistentOverload]
|
|
1880
1840
|
"""Call the function with the values.
|
|
1881
1841
|
|
|
1882
1842
|
Args:
|
|
@@ -1885,17 +1845,17 @@ class EventCallback(Generic[P, T]):
|
|
|
1885
1845
|
Returns:
|
|
1886
1846
|
The function with the values.
|
|
1887
1847
|
"""
|
|
1888
|
-
return self.func(*values) #
|
|
1848
|
+
return self.func(*values) # pyright: ignore [reportCallIssue, reportReturnType]
|
|
1889
1849
|
|
|
1890
1850
|
@overload
|
|
1891
1851
|
def __get__(
|
|
1892
|
-
self: EventCallback[P, T], instance: None, owner
|
|
1852
|
+
self: EventCallback[P, T], instance: None, owner: Any
|
|
1893
1853
|
) -> EventCallback[P, T]: ...
|
|
1894
1854
|
|
|
1895
1855
|
@overload
|
|
1896
|
-
def __get__(self, instance, owner) -> Callable[P, T]: ...
|
|
1856
|
+
def __get__(self, instance: Any, owner: Any) -> Callable[P, T]: ...
|
|
1897
1857
|
|
|
1898
|
-
def __get__(self, instance, owner) -> Callable:
|
|
1858
|
+
def __get__(self, instance: Any, owner: Any) -> Callable:
|
|
1899
1859
|
"""Get the function with the instance bound to it.
|
|
1900
1860
|
|
|
1901
1861
|
Args:
|
|
@@ -1906,9 +1866,9 @@ class EventCallback(Generic[P, T]):
|
|
|
1906
1866
|
The function with the instance bound to it
|
|
1907
1867
|
"""
|
|
1908
1868
|
if instance is None:
|
|
1909
|
-
return self.func
|
|
1869
|
+
return self.func
|
|
1910
1870
|
|
|
1911
|
-
return partial(self.func, instance)
|
|
1871
|
+
return partial(self.func, instance)
|
|
1912
1872
|
|
|
1913
1873
|
|
|
1914
1874
|
G = ParamSpec("G")
|
|
@@ -1959,7 +1919,7 @@ class EventNamespace(types.SimpleNamespace):
|
|
|
1959
1919
|
@staticmethod
|
|
1960
1920
|
def __call__(
|
|
1961
1921
|
func: None = None, *, background: bool | None = None
|
|
1962
|
-
) -> Callable[[Callable[Concatenate[BASE_STATE, P], T]], EventCallback[P, T]]: ...
|
|
1922
|
+
) -> Callable[[Callable[Concatenate[BASE_STATE, P], T]], EventCallback[P, T]]: ... # pyright: ignore [reportInvalidTypeVarUse]
|
|
1963
1923
|
|
|
1964
1924
|
@overload
|
|
1965
1925
|
@staticmethod
|
|
@@ -1984,6 +1944,9 @@ class EventNamespace(types.SimpleNamespace):
|
|
|
1984
1944
|
func: The function to wrap.
|
|
1985
1945
|
background: Whether the event should be run in the background. Defaults to False.
|
|
1986
1946
|
|
|
1947
|
+
Raises:
|
|
1948
|
+
TypeError: If background is True and the function is not a coroutine or async generator. # noqa: DAR402
|
|
1949
|
+
|
|
1987
1950
|
Returns:
|
|
1988
1951
|
The wrapped function.
|
|
1989
1952
|
"""
|
|
@@ -1992,8 +1955,14 @@ class EventNamespace(types.SimpleNamespace):
|
|
|
1992
1955
|
func: Callable[Concatenate[BASE_STATE, P], T],
|
|
1993
1956
|
) -> EventCallback[P, T]:
|
|
1994
1957
|
if background is True:
|
|
1995
|
-
|
|
1996
|
-
|
|
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]
|
|
1997
1966
|
|
|
1998
1967
|
if func is not None:
|
|
1999
1968
|
return wrapper(func)
|