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.
- 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 -115
- 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 -20
- 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 +137 -163
- 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 +34 -39
- reflex/utils/build.py +6 -2
- reflex/utils/codespaces.py +1 -4
- reflex/utils/compat.py +6 -5
- reflex/utils/console.py +52 -21
- 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.8a1.dist-info → reflex-0.7.0a1.dist-info}/METADATA +7 -10
- {reflex-0.6.8a1.dist-info → reflex-0.7.0a1.dist-info}/RECORD +153 -150
- {reflex-0.6.8a1.dist-info → reflex-0.7.0a1.dist-info}/WHEEL +1 -1
- reflex/experimental/assets.py +0 -37
- reflex/proxy.py +0 -119
- {reflex-0.6.8a1.dist-info → reflex-0.7.0a1.dist-info}/LICENSE +0 -0
- {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
|
|
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
|
|
|
@@ -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(
|
|
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
|
-
|
|
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:
|
|
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(
|
|
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 ()
|
|
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]]]: ...
|
|
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]: #
|
|
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]
|
|
651
|
+
return_annotation = Tuple[inner_type]
|
|
666
652
|
|
|
667
|
-
inner.__signature__ = inspect.signature(inner).replace( #
|
|
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
|
-
)
|
|
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:
|
|
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
|
-
)
|
|
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(
|
|
1058
|
+
url = cond(
|
|
1108
1059
|
is_data_url,
|
|
1109
1060
|
data.to(str),
|
|
1110
|
-
"data:text/plain," + data.to_string(),
|
|
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
|
-
|
|
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
|
-
#
|
|
1279
|
-
|
|
1234
|
+
#noqa: DAR401
|
|
1280
1235
|
"""
|
|
1281
|
-
event_spec_args = parse_args_spec(event_spec)
|
|
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
|
-
|
|
1318
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1361
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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)
|
|
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
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
|
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[
|
|
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}
|
|
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
|
-
|
|
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) #
|
|
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
|
-
|
|
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) #
|
|
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) #
|
|
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(
|
|
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: #
|
|
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) #
|
|
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:
|
|
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
|
|
1869
|
+
return self.func
|
|
1905
1870
|
|
|
1906
|
-
return partial(self.func, instance)
|
|
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
|
-
|
|
1991
|
-
|
|
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)
|