reflex 0.6.8a2__py3-none-any.whl → 0.7.0a2__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 +3 -3
- 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 +249 -116
- 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/app_wrap.pyi +16 -16
- reflex/components/base/bare.py +1 -1
- reflex/components/base/body.pyi +16 -16
- reflex/components/base/document.pyi +76 -76
- reflex/components/base/error_boundary.py +2 -1
- reflex/components/base/error_boundary.pyi +19 -22
- reflex/components/base/fragment.pyi +16 -16
- reflex/components/base/head.pyi +31 -31
- reflex/components/base/link.pyi +31 -31
- reflex/components/base/meta.py +2 -2
- reflex/components/base/meta.pyi +61 -61
- reflex/components/base/script.pyi +19 -19
- 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 +159 -4
- reflex/components/core/banner.pyi +162 -76
- reflex/components/core/breakpoints.py +3 -1
- reflex/components/core/client_side_routing.py +1 -1
- reflex/components/core/client_side_routing.pyi +32 -32
- reflex/components/core/clipboard.pyi +17 -20
- reflex/components/core/cond.py +9 -10
- reflex/components/core/debounce.py +1 -1
- reflex/components/core/debounce.pyi +17 -17
- reflex/components/core/foreach.py +28 -3
- reflex/components/core/html.py +1 -1
- reflex/components/core/html.pyi +16 -16
- 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/core/upload.pyi +80 -88
- reflex/components/datadisplay/code.py +5 -14
- reflex/components/datadisplay/code.pyi +31 -31
- reflex/components/datadisplay/dataeditor.py +7 -4
- reflex/components/datadisplay/dataeditor.pyi +40 -54
- reflex/components/datadisplay/logo.py +13 -8
- reflex/components/datadisplay/shiki_code_block.py +14 -9
- reflex/components/datadisplay/shiki_code_block.pyi +46 -46
- reflex/components/dynamic.py +22 -3
- reflex/components/el/constants/reflex.py +1 -1
- reflex/components/el/element.py +1 -1
- reflex/components/el/element.pyi +16 -16
- reflex/components/el/elements/base.pyi +16 -16
- reflex/components/el/elements/forms.py +4 -4
- reflex/components/el/elements/forms.pyi +224 -258
- reflex/components/el/elements/inline.pyi +421 -421
- reflex/components/el/elements/media.pyi +376 -376
- reflex/components/el/elements/metadata.pyi +91 -91
- reflex/components/el/elements/other.pyi +106 -106
- reflex/components/el/elements/scripts.pyi +46 -46
- reflex/components/el/elements/sectioning.pyi +226 -226
- reflex/components/el/elements/tables.pyi +151 -151
- reflex/components/el/elements/typography.pyi +226 -226
- reflex/components/gridjs/datatable.pyi +31 -31
- reflex/components/lucide/icon.py +46 -8
- reflex/components/lucide/icon.pyi +85 -31
- reflex/components/markdown/markdown.py +10 -8
- reflex/components/markdown/markdown.pyi +16 -16
- reflex/components/moment/moment.py +2 -2
- reflex/components/moment/moment.pyi +17 -19
- reflex/components/next/base.pyi +16 -16
- reflex/components/next/image.py +16 -4
- reflex/components/next/image.pyi +22 -20
- reflex/components/next/link.py +1 -1
- reflex/components/next/link.pyi +16 -16
- reflex/components/next/video.pyi +16 -16
- reflex/components/plotly/plotly.py +5 -5
- reflex/components/plotly/plotly.pyi +34 -44
- 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 +110 -108
- reflex/components/radix/primitives/base.pyi +31 -31
- reflex/components/radix/primitives/drawer.py +5 -2
- reflex/components/radix/primitives/drawer.pyi +179 -187
- reflex/components/radix/primitives/form.pyi +160 -172
- reflex/components/radix/primitives/progress.py +1 -1
- reflex/components/radix/primitives/progress.pyi +76 -76
- reflex/components/radix/primitives/slider.py +1 -1
- reflex/components/radix/primitives/slider.pyi +78 -82
- reflex/components/radix/themes/base.pyi +121 -121
- reflex/components/radix/themes/color_mode.py +11 -9
- reflex/components/radix/themes/color_mode.pyi +47 -49
- reflex/components/radix/themes/components/alert_dialog.py +3 -0
- reflex/components/radix/themes/components/alert_dialog.pyi +110 -112
- reflex/components/radix/themes/components/aspect_ratio.pyi +16 -16
- reflex/components/radix/themes/components/avatar.pyi +16 -16
- reflex/components/radix/themes/components/badge.pyi +16 -16
- reflex/components/radix/themes/components/button.pyi +16 -16
- reflex/components/radix/themes/components/callout.pyi +76 -76
- reflex/components/radix/themes/components/card.py +1 -1
- reflex/components/radix/themes/components/card.pyi +17 -17
- reflex/components/radix/themes/components/checkbox.pyi +49 -55
- reflex/components/radix/themes/components/checkbox_cards.pyi +31 -31
- reflex/components/radix/themes/components/checkbox_group.pyi +31 -31
- reflex/components/radix/themes/components/context_menu.py +5 -0
- reflex/components/radix/themes/components/context_menu.pyi +149 -155
- reflex/components/radix/themes/components/data_list.pyi +61 -61
- reflex/components/radix/themes/components/dialog.py +3 -0
- reflex/components/radix/themes/components/dialog.pyi +113 -117
- reflex/components/radix/themes/components/dropdown_menu.py +5 -0
- reflex/components/radix/themes/components/dropdown_menu.pyi +133 -137
- reflex/components/radix/themes/components/hover_card.py +3 -0
- reflex/components/radix/themes/components/hover_card.pyi +63 -67
- reflex/components/radix/themes/components/icon_button.py +2 -2
- reflex/components/radix/themes/components/icon_button.pyi +17 -16
- reflex/components/radix/themes/components/inset.pyi +16 -16
- reflex/components/radix/themes/components/popover.py +3 -0
- reflex/components/radix/themes/components/popover.pyi +68 -70
- reflex/components/radix/themes/components/progress.pyi +16 -16
- reflex/components/radix/themes/components/radio.pyi +16 -16
- reflex/components/radix/themes/components/radio_cards.py +2 -0
- reflex/components/radix/themes/components/radio_cards.pyi +32 -34
- reflex/components/radix/themes/components/radio_group.py +1 -1
- reflex/components/radix/themes/components/radio_group.pyi +62 -64
- reflex/components/radix/themes/components/scroll_area.pyi +16 -16
- reflex/components/radix/themes/components/segmented_control.pyi +32 -35
- reflex/components/radix/themes/components/select.py +4 -0
- reflex/components/radix/themes/components/select.pyi +145 -157
- reflex/components/radix/themes/components/separator.pyi +16 -16
- reflex/components/radix/themes/components/skeleton.py +3 -0
- reflex/components/radix/themes/components/skeleton.pyi +16 -16
- reflex/components/radix/themes/components/slider.pyi +22 -28
- reflex/components/radix/themes/components/spinner.pyi +16 -16
- reflex/components/radix/themes/components/switch.pyi +17 -19
- reflex/components/radix/themes/components/table.pyi +106 -106
- reflex/components/radix/themes/components/tabs.py +3 -0
- reflex/components/radix/themes/components/tabs.pyi +78 -82
- reflex/components/radix/themes/components/text_area.py +12 -0
- reflex/components/radix/themes/components/text_area.pyi +21 -33
- reflex/components/radix/themes/components/text_field.py +1 -1
- reflex/components/radix/themes/components/text_field.pyi +52 -80
- reflex/components/radix/themes/components/tooltip.py +6 -1
- reflex/components/radix/themes/components/tooltip.pyi +20 -21
- reflex/components/radix/themes/layout/__init__.pyi +1 -1
- reflex/components/radix/themes/layout/base.pyi +16 -16
- reflex/components/radix/themes/layout/box.pyi +16 -16
- reflex/components/radix/themes/layout/center.pyi +16 -16
- reflex/components/radix/themes/layout/container.pyi +16 -16
- reflex/components/radix/themes/layout/flex.pyi +16 -16
- reflex/components/radix/themes/layout/grid.pyi +16 -16
- reflex/components/radix/themes/layout/list.py +2 -2
- reflex/components/radix/themes/layout/list.pyi +76 -76
- reflex/components/radix/themes/layout/section.pyi +16 -16
- reflex/components/radix/themes/layout/spacer.pyi +16 -16
- reflex/components/radix/themes/layout/stack.py +2 -2
- reflex/components/radix/themes/layout/stack.pyi +46 -46
- reflex/components/radix/themes/typography/blockquote.pyi +16 -16
- reflex/components/radix/themes/typography/code.pyi +16 -16
- reflex/components/radix/themes/typography/heading.pyi +16 -16
- reflex/components/radix/themes/typography/link.py +1 -1
- reflex/components/radix/themes/typography/link.pyi +16 -16
- reflex/components/radix/themes/typography/text.py +2 -2
- reflex/components/radix/themes/typography/text.pyi +106 -106
- reflex/components/react_player/audio.pyi +33 -39
- reflex/components/react_player/react_player.py +1 -1
- reflex/components/react_player/react_player.pyi +32 -38
- reflex/components/react_player/video.pyi +33 -39
- reflex/components/recharts/__init__.py +2 -0
- reflex/components/recharts/__init__.pyi +2 -0
- reflex/components/recharts/cartesian.pyi +282 -282
- reflex/components/recharts/charts.py +15 -15
- reflex/components/recharts/charts.pyi +164 -164
- reflex/components/recharts/general.py +19 -4
- reflex/components/recharts/general.pyi +132 -81
- reflex/components/recharts/polar.py +2 -2
- reflex/components/recharts/polar.pyi +55 -55
- reflex/components/recharts/recharts.py +4 -4
- reflex/components/recharts/recharts.pyi +31 -31
- reflex/components/sonner/toast.py +15 -13
- reflex/components/sonner/toast.pyi +22 -22
- reflex/components/suneditor/editor.py +6 -4
- reflex/components/suneditor/editor.pyi +26 -40
- 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 +215 -208
- 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 +79 -83
- 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 +5 -5
- 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 +89 -26
- reflex/utils/exec.py +69 -74
- reflex/utils/export.py +6 -1
- reflex/utils/format.py +8 -40
- reflex/utils/imports.py +2 -2
- reflex/utils/lazy_loader.py +7 -1
- reflex/utils/path_ops.py +28 -14
- reflex/utils/prerequisites.py +326 -67
- reflex/utils/processes.py +45 -32
- reflex/utils/pyi_generator.py +39 -33
- 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 +656 -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.0a2.dist-info}/METADATA +7 -8
- reflex-0.7.0a2.dist-info/RECORD +401 -0
- {reflex-0.6.8a2.dist-info → reflex-0.7.0a2.dist-info}/WHEEL +1 -1
- reflex/experimental/assets.py +0 -37
- reflex-0.6.8a2.dist-info/RECORD +0 -397
- {reflex-0.6.8a2.dist-info → reflex-0.7.0a2.dist-info}/LICENSE +0 -0
- {reflex-0.6.8a2.dist-info → reflex-0.7.0a2.dist-info}/entry_points.txt +0 -0
reflex/vars/base.py
CHANGED
|
@@ -5,14 +5,13 @@ from __future__ import annotations
|
|
|
5
5
|
import contextlib
|
|
6
6
|
import dataclasses
|
|
7
7
|
import datetime
|
|
8
|
-
import dis
|
|
9
8
|
import functools
|
|
10
9
|
import inspect
|
|
11
10
|
import json
|
|
12
11
|
import random
|
|
13
12
|
import re
|
|
14
13
|
import string
|
|
15
|
-
import
|
|
14
|
+
import uuid
|
|
16
15
|
import warnings
|
|
17
16
|
from types import CodeType, FunctionType
|
|
18
17
|
from typing import (
|
|
@@ -20,14 +19,17 @@ from typing import (
|
|
|
20
19
|
Any,
|
|
21
20
|
Callable,
|
|
22
21
|
ClassVar,
|
|
22
|
+
Coroutine,
|
|
23
23
|
Dict,
|
|
24
24
|
FrozenSet,
|
|
25
25
|
Generic,
|
|
26
26
|
Iterable,
|
|
27
27
|
List,
|
|
28
28
|
Literal,
|
|
29
|
+
Mapping,
|
|
29
30
|
NoReturn,
|
|
30
31
|
Optional,
|
|
32
|
+
Sequence,
|
|
31
33
|
Set,
|
|
32
34
|
Tuple,
|
|
33
35
|
Type,
|
|
@@ -38,6 +40,7 @@ from typing import (
|
|
|
38
40
|
overload,
|
|
39
41
|
)
|
|
40
42
|
|
|
43
|
+
from sqlalchemy.orm import DeclarativeBase
|
|
41
44
|
from typing_extensions import ParamSpec, TypeGuard, deprecated, get_type_hints, override
|
|
42
45
|
|
|
43
46
|
from reflex import constants
|
|
@@ -45,10 +48,11 @@ from reflex.base import Base
|
|
|
45
48
|
from reflex.constants.compiler import Hooks
|
|
46
49
|
from reflex.utils import console, exceptions, imports, serializers, types
|
|
47
50
|
from reflex.utils.exceptions import (
|
|
51
|
+
ComputedVarSignatureError,
|
|
52
|
+
UntypedComputedVarError,
|
|
48
53
|
VarAttributeError,
|
|
49
54
|
VarDependencyError,
|
|
50
55
|
VarTypeError,
|
|
51
|
-
VarValueError,
|
|
52
56
|
)
|
|
53
57
|
from reflex.utils.format import format_state_name
|
|
54
58
|
from reflex.utils.imports import (
|
|
@@ -64,6 +68,7 @@ from reflex.utils.types import (
|
|
|
64
68
|
_isinstance,
|
|
65
69
|
get_origin,
|
|
66
70
|
has_args,
|
|
71
|
+
safe_issubclass,
|
|
67
72
|
unionize,
|
|
68
73
|
)
|
|
69
74
|
|
|
@@ -77,6 +82,8 @@ if TYPE_CHECKING:
|
|
|
77
82
|
|
|
78
83
|
VAR_TYPE = TypeVar("VAR_TYPE", covariant=True)
|
|
79
84
|
OTHER_VAR_TYPE = TypeVar("OTHER_VAR_TYPE")
|
|
85
|
+
STRING_T = TypeVar("STRING_T", bound=str)
|
|
86
|
+
SEQUENCE_TYPE = TypeVar("SEQUENCE_TYPE", bound=Sequence)
|
|
80
87
|
|
|
81
88
|
warnings.filterwarnings("ignore", message="fields may not start with an underscore")
|
|
82
89
|
|
|
@@ -127,7 +134,7 @@ class VarData:
|
|
|
127
134
|
state: str = "",
|
|
128
135
|
field_name: str = "",
|
|
129
136
|
imports: ImportDict | ParsedImportDict | None = None,
|
|
130
|
-
hooks:
|
|
137
|
+
hooks: Mapping[str, VarData | None] | Sequence[str] | str | None = None,
|
|
131
138
|
deps: list[Var] | None = None,
|
|
132
139
|
position: Hooks.HookPosition | None = None,
|
|
133
140
|
):
|
|
@@ -141,10 +148,12 @@ class VarData:
|
|
|
141
148
|
deps: Dependencies of the var for useCallback.
|
|
142
149
|
position: Position of the hook in the component.
|
|
143
150
|
"""
|
|
151
|
+
if isinstance(hooks, str):
|
|
152
|
+
hooks = [hooks]
|
|
153
|
+
if not isinstance(hooks, dict):
|
|
154
|
+
hooks = {hook: None for hook in (hooks or [])}
|
|
144
155
|
immutable_imports: ImmutableParsedImportDict = tuple(
|
|
145
|
-
|
|
146
|
-
((k, tuple(sorted(v))) for k, v in parse_imports(imports or {}).items())
|
|
147
|
-
)
|
|
156
|
+
(k, tuple(v)) for k, v in parse_imports(imports or {}).items()
|
|
148
157
|
)
|
|
149
158
|
object.__setattr__(self, "state", state)
|
|
150
159
|
object.__setattr__(self, "field_name", field_name)
|
|
@@ -153,6 +162,16 @@ class VarData:
|
|
|
153
162
|
object.__setattr__(self, "deps", tuple(deps or []))
|
|
154
163
|
object.__setattr__(self, "position", position or None)
|
|
155
164
|
|
|
165
|
+
if hooks and any(hooks.values()):
|
|
166
|
+
merged_var_data = VarData.merge(self, *hooks.values())
|
|
167
|
+
if merged_var_data is not None:
|
|
168
|
+
object.__setattr__(self, "state", merged_var_data.state)
|
|
169
|
+
object.__setattr__(self, "field_name", merged_var_data.field_name)
|
|
170
|
+
object.__setattr__(self, "imports", merged_var_data.imports)
|
|
171
|
+
object.__setattr__(self, "hooks", merged_var_data.hooks)
|
|
172
|
+
object.__setattr__(self, "deps", merged_var_data.deps)
|
|
173
|
+
object.__setattr__(self, "position", merged_var_data.position)
|
|
174
|
+
|
|
156
175
|
def old_school_imports(self) -> ImportDict:
|
|
157
176
|
"""Return the imports as a mutable dict.
|
|
158
177
|
|
|
@@ -432,7 +451,7 @@ class Var(Generic[VAR_TYPE]):
|
|
|
432
451
|
@dataclasses.dataclass(
|
|
433
452
|
eq=False,
|
|
434
453
|
frozen=True,
|
|
435
|
-
|
|
454
|
+
slots=True,
|
|
436
455
|
)
|
|
437
456
|
class ToVarOperation(ToOperation, cls):
|
|
438
457
|
"""Base class of converting a var to another var type."""
|
|
@@ -443,7 +462,12 @@ class Var(Generic[VAR_TYPE]):
|
|
|
443
462
|
|
|
444
463
|
_default_var_type: ClassVar[GenericType] = default_type
|
|
445
464
|
|
|
446
|
-
|
|
465
|
+
new_to_var_operation_name = f"To{cls.__name__.removesuffix('Var')}Operation"
|
|
466
|
+
ToVarOperation.__qualname__ = (
|
|
467
|
+
ToVarOperation.__qualname__.removesuffix(ToVarOperation.__name__)
|
|
468
|
+
+ new_to_var_operation_name
|
|
469
|
+
)
|
|
470
|
+
ToVarOperation.__name__ = new_to_var_operation_name
|
|
447
471
|
|
|
448
472
|
_var_subclasses.append(VarSubclassEntry(cls, ToVarOperation, python_types))
|
|
449
473
|
|
|
@@ -492,20 +516,30 @@ class Var(Generic[VAR_TYPE]):
|
|
|
492
516
|
|
|
493
517
|
@overload
|
|
494
518
|
def _replace(
|
|
495
|
-
self,
|
|
519
|
+
self,
|
|
520
|
+
_var_type: Type[OTHER_VAR_TYPE],
|
|
521
|
+
merge_var_data: VarData | None = None,
|
|
522
|
+
**kwargs: Any,
|
|
496
523
|
) -> Var[OTHER_VAR_TYPE]: ...
|
|
497
524
|
|
|
498
525
|
@overload
|
|
499
526
|
def _replace(
|
|
500
|
-
self,
|
|
527
|
+
self,
|
|
528
|
+
_var_type: GenericType | None = None,
|
|
529
|
+
merge_var_data: VarData | None = None,
|
|
530
|
+
**kwargs: Any,
|
|
501
531
|
) -> Self: ...
|
|
502
532
|
|
|
503
533
|
def _replace(
|
|
504
|
-
self,
|
|
534
|
+
self,
|
|
535
|
+
_var_type: GenericType | None = None,
|
|
536
|
+
merge_var_data: VarData | None = None,
|
|
537
|
+
**kwargs: Any,
|
|
505
538
|
) -> Self | Var:
|
|
506
539
|
"""Make a copy of this Var with updated fields.
|
|
507
540
|
|
|
508
541
|
Args:
|
|
542
|
+
_var_type: The new type of the Var.
|
|
509
543
|
merge_var_data: VarData to merge into the existing VarData.
|
|
510
544
|
**kwargs: Var fields to update.
|
|
511
545
|
|
|
@@ -539,56 +573,89 @@ class Var(Generic[VAR_TYPE]):
|
|
|
539
573
|
|
|
540
574
|
return value_with_replaced
|
|
541
575
|
|
|
576
|
+
@overload
|
|
577
|
+
@classmethod
|
|
578
|
+
def create( # pyright: ignore[reportOverlappingOverload]
|
|
579
|
+
cls,
|
|
580
|
+
value: bool,
|
|
581
|
+
_var_data: VarData | None = None,
|
|
582
|
+
) -> BooleanVar: ...
|
|
583
|
+
|
|
584
|
+
@overload
|
|
542
585
|
@classmethod
|
|
543
586
|
def create(
|
|
544
587
|
cls,
|
|
545
|
-
value:
|
|
546
|
-
_var_is_local: bool | None = None,
|
|
547
|
-
_var_is_string: bool | None = None,
|
|
588
|
+
value: int,
|
|
548
589
|
_var_data: VarData | None = None,
|
|
549
|
-
) ->
|
|
590
|
+
) -> NumberVar[int]: ...
|
|
591
|
+
|
|
592
|
+
@overload
|
|
593
|
+
@classmethod
|
|
594
|
+
def create(
|
|
595
|
+
cls,
|
|
596
|
+
value: float,
|
|
597
|
+
_var_data: VarData | None = None,
|
|
598
|
+
) -> NumberVar[float]: ...
|
|
599
|
+
|
|
600
|
+
@overload
|
|
601
|
+
@classmethod
|
|
602
|
+
def create( # pyright: ignore [reportOverlappingOverload]
|
|
603
|
+
cls,
|
|
604
|
+
value: STRING_T,
|
|
605
|
+
_var_data: VarData | None = None,
|
|
606
|
+
) -> StringVar[STRING_T]: ...
|
|
607
|
+
|
|
608
|
+
@overload
|
|
609
|
+
@classmethod
|
|
610
|
+
def create( # pyright: ignore[reportOverlappingOverload]
|
|
611
|
+
cls,
|
|
612
|
+
value: None,
|
|
613
|
+
_var_data: VarData | None = None,
|
|
614
|
+
) -> NoneVar: ...
|
|
615
|
+
|
|
616
|
+
@overload
|
|
617
|
+
@classmethod
|
|
618
|
+
def create(
|
|
619
|
+
cls,
|
|
620
|
+
value: MAPPING_TYPE,
|
|
621
|
+
_var_data: VarData | None = None,
|
|
622
|
+
) -> ObjectVar[MAPPING_TYPE]: ...
|
|
623
|
+
|
|
624
|
+
@overload
|
|
625
|
+
@classmethod
|
|
626
|
+
def create(
|
|
627
|
+
cls,
|
|
628
|
+
value: SEQUENCE_TYPE,
|
|
629
|
+
_var_data: VarData | None = None,
|
|
630
|
+
) -> ArrayVar[SEQUENCE_TYPE]: ...
|
|
631
|
+
|
|
632
|
+
@overload
|
|
633
|
+
@classmethod
|
|
634
|
+
def create(
|
|
635
|
+
cls,
|
|
636
|
+
value: OTHER_VAR_TYPE,
|
|
637
|
+
_var_data: VarData | None = None,
|
|
638
|
+
) -> Var[OTHER_VAR_TYPE]: ...
|
|
639
|
+
|
|
640
|
+
@classmethod
|
|
641
|
+
def create(
|
|
642
|
+
cls,
|
|
643
|
+
value: OTHER_VAR_TYPE,
|
|
644
|
+
_var_data: VarData | None = None,
|
|
645
|
+
) -> Var[OTHER_VAR_TYPE]:
|
|
550
646
|
"""Create a var from a value.
|
|
551
647
|
|
|
552
648
|
Args:
|
|
553
649
|
value: The value to create the var from.
|
|
554
|
-
_var_is_local: Whether the var is local. Deprecated.
|
|
555
|
-
_var_is_string: Whether the var is a string literal. Deprecated.
|
|
556
650
|
_var_data: Additional hooks and imports associated with the Var.
|
|
557
651
|
|
|
558
652
|
Returns:
|
|
559
653
|
The var.
|
|
560
654
|
"""
|
|
561
|
-
if _var_is_local is not None:
|
|
562
|
-
console.deprecate(
|
|
563
|
-
feature_name="_var_is_local",
|
|
564
|
-
reason="The _var_is_local argument is not supported for Var."
|
|
565
|
-
"If you want to create a Var from a raw Javascript expression, use the constructor directly",
|
|
566
|
-
deprecation_version="0.6.0",
|
|
567
|
-
removal_version="0.7.0",
|
|
568
|
-
)
|
|
569
|
-
if _var_is_string is not None:
|
|
570
|
-
console.deprecate(
|
|
571
|
-
feature_name="_var_is_string",
|
|
572
|
-
reason="The _var_is_string argument is not supported for Var."
|
|
573
|
-
"If you want to create a Var from a raw Javascript expression, use the constructor directly",
|
|
574
|
-
deprecation_version="0.6.0",
|
|
575
|
-
removal_version="0.7.0",
|
|
576
|
-
)
|
|
577
|
-
|
|
578
655
|
# If the value is already a var, do nothing.
|
|
579
656
|
if isinstance(value, Var):
|
|
580
657
|
return value
|
|
581
658
|
|
|
582
|
-
# Try to pull the imports and hooks from contained values.
|
|
583
|
-
if not isinstance(value, str):
|
|
584
|
-
return LiteralVar.create(value, _var_data=_var_data)
|
|
585
|
-
|
|
586
|
-
if _var_is_string is False or _var_is_local is True:
|
|
587
|
-
return cls(
|
|
588
|
-
_js_expr=value,
|
|
589
|
-
_var_data=_var_data,
|
|
590
|
-
)
|
|
591
|
-
|
|
592
659
|
return LiteralVar.create(value, _var_data=_var_data)
|
|
593
660
|
|
|
594
661
|
@classmethod
|
|
@@ -643,8 +710,8 @@ class Var(Generic[VAR_TYPE]):
|
|
|
643
710
|
@overload
|
|
644
711
|
def to(
|
|
645
712
|
self,
|
|
646
|
-
output: type[
|
|
647
|
-
) -> ObjectVar[
|
|
713
|
+
output: type[MAPPING_TYPE],
|
|
714
|
+
) -> ObjectVar[MAPPING_TYPE]: ...
|
|
648
715
|
|
|
649
716
|
@overload
|
|
650
717
|
def to(
|
|
@@ -686,14 +753,16 @@ class Var(Generic[VAR_TYPE]):
|
|
|
686
753
|
|
|
687
754
|
# If the first argument is a python type, we map it to the corresponding Var type.
|
|
688
755
|
for var_subclass in _var_subclasses[::-1]:
|
|
689
|
-
if fixed_output_type in var_subclass.python_types
|
|
756
|
+
if fixed_output_type in var_subclass.python_types or safe_issubclass(
|
|
757
|
+
fixed_output_type, var_subclass.python_types
|
|
758
|
+
):
|
|
690
759
|
return self.to(var_subclass.var_subclass, output)
|
|
691
760
|
|
|
692
761
|
if fixed_output_type is None:
|
|
693
|
-
return get_to_operation(NoneVar).create(self) #
|
|
762
|
+
return get_to_operation(NoneVar).create(self) # pyright: ignore [reportReturnType]
|
|
694
763
|
|
|
695
764
|
# Handle fixed_output_type being Base or a dataclass.
|
|
696
|
-
if can_use_in_object_var(
|
|
765
|
+
if can_use_in_object_var(output):
|
|
697
766
|
return self.to(ObjectVar, output)
|
|
698
767
|
|
|
699
768
|
if inspect.isclass(output):
|
|
@@ -707,7 +776,7 @@ class Var(Generic[VAR_TYPE]):
|
|
|
707
776
|
to_operation_return = var_subclass.to_var_subclass.create(
|
|
708
777
|
value=self, _var_type=new_var_type
|
|
709
778
|
)
|
|
710
|
-
return to_operation_return #
|
|
779
|
+
return to_operation_return # pyright: ignore [reportReturnType]
|
|
711
780
|
|
|
712
781
|
# If we can't determine the first argument, we just replace the _var_type.
|
|
713
782
|
if not issubclass(output, Var) or var_type is None:
|
|
@@ -725,6 +794,9 @@ class Var(Generic[VAR_TYPE]):
|
|
|
725
794
|
|
|
726
795
|
return self
|
|
727
796
|
|
|
797
|
+
@overload
|
|
798
|
+
def guess_type(self: Var[NoReturn]) -> Var[Any]: ... # pyright: ignore [reportOverlappingOverload]
|
|
799
|
+
|
|
728
800
|
@overload
|
|
729
801
|
def guess_type(self: Var[str]) -> StringVar: ...
|
|
730
802
|
|
|
@@ -734,6 +806,9 @@ class Var(Generic[VAR_TYPE]):
|
|
|
734
806
|
@overload
|
|
735
807
|
def guess_type(self: Var[int] | Var[float] | Var[int | float]) -> NumberVar: ...
|
|
736
808
|
|
|
809
|
+
@overload
|
|
810
|
+
def guess_type(self: Var[BASE_TYPE]) -> ObjectVar[BASE_TYPE]: ...
|
|
811
|
+
|
|
737
812
|
@overload
|
|
738
813
|
def guess_type(self) -> Self: ...
|
|
739
814
|
|
|
@@ -820,7 +895,7 @@ class Var(Generic[VAR_TYPE]):
|
|
|
820
895
|
return False
|
|
821
896
|
if issubclass(type_, list):
|
|
822
897
|
return []
|
|
823
|
-
if issubclass(type_,
|
|
898
|
+
if issubclass(type_, Mapping):
|
|
824
899
|
return {}
|
|
825
900
|
if issubclass(type_, tuple):
|
|
826
901
|
return ()
|
|
@@ -882,7 +957,7 @@ class Var(Generic[VAR_TYPE]):
|
|
|
882
957
|
|
|
883
958
|
return setter
|
|
884
959
|
|
|
885
|
-
def _var_set_state(self, state: type[BaseState] | str):
|
|
960
|
+
def _var_set_state(self, state: type[BaseState] | str) -> Self:
|
|
886
961
|
"""Set the state of the var.
|
|
887
962
|
|
|
888
963
|
Args:
|
|
@@ -897,7 +972,7 @@ class Var(Generic[VAR_TYPE]):
|
|
|
897
972
|
else format_state_name(state.get_full_name())
|
|
898
973
|
)
|
|
899
974
|
|
|
900
|
-
return StateOperation.create(
|
|
975
|
+
return StateOperation.create( # pyright: ignore [reportReturnType]
|
|
901
976
|
formatted_state_name,
|
|
902
977
|
self,
|
|
903
978
|
_var_data=VarData.merge(
|
|
@@ -1026,7 +1101,7 @@ class Var(Generic[VAR_TYPE]):
|
|
|
1026
1101
|
f"$/{constants.Dirs.STATE_PATH}": [imports.ImportVar(tag="refs")]
|
|
1027
1102
|
}
|
|
1028
1103
|
),
|
|
1029
|
-
).to(ObjectVar,
|
|
1104
|
+
).to(ObjectVar, Mapping[str, str])
|
|
1030
1105
|
return refs[LiteralVar.create(str(self))]
|
|
1031
1106
|
|
|
1032
1107
|
@deprecated("Use `.js_type()` instead.")
|
|
@@ -1076,43 +1151,6 @@ class Var(Generic[VAR_TYPE]):
|
|
|
1076
1151
|
"""
|
|
1077
1152
|
return self
|
|
1078
1153
|
|
|
1079
|
-
def __getattr__(self, name: str):
|
|
1080
|
-
"""Get an attribute of the var.
|
|
1081
|
-
|
|
1082
|
-
Args:
|
|
1083
|
-
name: The name of the attribute.
|
|
1084
|
-
|
|
1085
|
-
Returns:
|
|
1086
|
-
The attribute.
|
|
1087
|
-
|
|
1088
|
-
Raises:
|
|
1089
|
-
VarAttributeError: If the attribute does not exist.
|
|
1090
|
-
TypeError: If the var type is Any.
|
|
1091
|
-
"""
|
|
1092
|
-
if name.startswith("_"):
|
|
1093
|
-
return self.__getattribute__(name)
|
|
1094
|
-
|
|
1095
|
-
if name == "contains":
|
|
1096
|
-
raise TypeError(
|
|
1097
|
-
f"Var of type {self._var_type} does not support contains check."
|
|
1098
|
-
)
|
|
1099
|
-
if name == "reverse":
|
|
1100
|
-
raise TypeError("Cannot reverse non-list var.")
|
|
1101
|
-
|
|
1102
|
-
if self._var_type is Any:
|
|
1103
|
-
raise TypeError(
|
|
1104
|
-
f"You must provide an annotation for the state var `{self!s}`. Annotation cannot be `{self._var_type}`."
|
|
1105
|
-
)
|
|
1106
|
-
|
|
1107
|
-
if name in REPLACED_NAMES:
|
|
1108
|
-
raise VarAttributeError(
|
|
1109
|
-
f"Field {name!r} was renamed to {REPLACED_NAMES[name]!r}"
|
|
1110
|
-
)
|
|
1111
|
-
|
|
1112
|
-
raise VarAttributeError(
|
|
1113
|
-
f"The State var has no attribute '{name}' or may have been annotated wrongly.",
|
|
1114
|
-
)
|
|
1115
|
-
|
|
1116
1154
|
def _decode(self) -> Any:
|
|
1117
1155
|
"""Decode Var as a python value.
|
|
1118
1156
|
|
|
@@ -1123,7 +1161,7 @@ class Var(Generic[VAR_TYPE]):
|
|
|
1123
1161
|
The decoded value or the Var name.
|
|
1124
1162
|
"""
|
|
1125
1163
|
if isinstance(self, LiteralVar):
|
|
1126
|
-
return self._var_value
|
|
1164
|
+
return self._var_value
|
|
1127
1165
|
try:
|
|
1128
1166
|
return json.loads(str(self))
|
|
1129
1167
|
except ValueError:
|
|
@@ -1174,36 +1212,76 @@ class Var(Generic[VAR_TYPE]):
|
|
|
1174
1212
|
|
|
1175
1213
|
return ArrayVar.range(first_endpoint, second_endpoint, step)
|
|
1176
1214
|
|
|
1177
|
-
|
|
1178
|
-
"""Raise exception if using Var in a boolean context.
|
|
1215
|
+
if not TYPE_CHECKING:
|
|
1179
1216
|
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
"""
|
|
1183
|
-
raise VarTypeError(
|
|
1184
|
-
f"Cannot convert Var {str(self)!r} to bool for use with `if`, `and`, `or`, and `not`. "
|
|
1185
|
-
"Instead use `rx.cond` and bitwise operators `&` (and), `|` (or), `~` (invert)."
|
|
1186
|
-
)
|
|
1217
|
+
def __getattr__(self, name: str):
|
|
1218
|
+
"""Get an attribute of the var.
|
|
1187
1219
|
|
|
1188
|
-
|
|
1189
|
-
|
|
1220
|
+
Args:
|
|
1221
|
+
name: The name of the attribute.
|
|
1190
1222
|
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
f"Cannot iterate over Var {str(self)!r}. Instead use `rx.foreach`."
|
|
1196
|
-
)
|
|
1223
|
+
Raises:
|
|
1224
|
+
VarAttributeError: If the attribute does not exist.
|
|
1225
|
+
UntypedVarError: If the var type is Any.
|
|
1226
|
+
TypeError: If the var type is Any.
|
|
1197
1227
|
|
|
1198
|
-
|
|
1199
|
-
|
|
1228
|
+
# noqa: DAR101 self
|
|
1229
|
+
"""
|
|
1230
|
+
if name.startswith("_"):
|
|
1231
|
+
raise VarAttributeError(f"Attribute {name} not found.")
|
|
1200
1232
|
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1233
|
+
if name == "contains":
|
|
1234
|
+
raise TypeError(
|
|
1235
|
+
f"Var of type {self._var_type} does not support contains check."
|
|
1236
|
+
)
|
|
1237
|
+
if name == "reverse":
|
|
1238
|
+
raise TypeError("Cannot reverse non-list var.")
|
|
1239
|
+
|
|
1240
|
+
if self._var_type is Any:
|
|
1241
|
+
raise exceptions.UntypedVarError(
|
|
1242
|
+
f"You must provide an annotation for the state var `{self!s}`. Annotation cannot be `{self._var_type}`."
|
|
1243
|
+
)
|
|
1244
|
+
|
|
1245
|
+
raise VarAttributeError(
|
|
1246
|
+
f"The State var has no attribute '{name}' or may have been annotated wrongly.",
|
|
1247
|
+
)
|
|
1248
|
+
|
|
1249
|
+
def __bool__(self) -> bool:
|
|
1250
|
+
"""Raise exception if using Var in a boolean context.
|
|
1251
|
+
|
|
1252
|
+
Raises:
|
|
1253
|
+
VarTypeError: when attempting to bool-ify the Var.
|
|
1254
|
+
|
|
1255
|
+
# noqa: DAR101 self
|
|
1256
|
+
"""
|
|
1257
|
+
raise VarTypeError(
|
|
1258
|
+
f"Cannot convert Var {str(self)!r} to bool for use with `if`, `and`, `or`, and `not`. "
|
|
1259
|
+
"Instead use `rx.cond` and bitwise operators `&` (and), `|` (or), `~` (invert)."
|
|
1260
|
+
)
|
|
1261
|
+
|
|
1262
|
+
def __iter__(self) -> Any:
|
|
1263
|
+
"""Raise exception if using Var in an iterable context.
|
|
1264
|
+
|
|
1265
|
+
Raises:
|
|
1266
|
+
VarTypeError: when attempting to iterate over the Var.
|
|
1267
|
+
|
|
1268
|
+
# noqa: DAR101 self
|
|
1269
|
+
"""
|
|
1270
|
+
raise VarTypeError(
|
|
1271
|
+
f"Cannot iterate over Var {str(self)!r}. Instead use `rx.foreach`."
|
|
1272
|
+
)
|
|
1273
|
+
|
|
1274
|
+
def __contains__(self, _: Any) -> Var:
|
|
1275
|
+
"""Override the 'in' operator to alert the user that it is not supported.
|
|
1276
|
+
|
|
1277
|
+
Raises:
|
|
1278
|
+
VarTypeError: the operation is not supported
|
|
1279
|
+
|
|
1280
|
+
# noqa: DAR101 self
|
|
1281
|
+
"""
|
|
1282
|
+
raise VarTypeError(
|
|
1283
|
+
"'in' operator not supported for Var types, use Var.contains() instead."
|
|
1284
|
+
)
|
|
1207
1285
|
|
|
1208
1286
|
|
|
1209
1287
|
OUTPUT = TypeVar("OUTPUT", bound=Var)
|
|
@@ -1250,7 +1328,7 @@ class ToOperation:
|
|
|
1250
1328
|
"""
|
|
1251
1329
|
return VarData.merge(
|
|
1252
1330
|
self._original._get_all_var_data(),
|
|
1253
|
-
self._var_data,
|
|
1331
|
+
self._var_data,
|
|
1254
1332
|
)
|
|
1255
1333
|
|
|
1256
1334
|
@classmethod
|
|
@@ -1271,10 +1349,10 @@ class ToOperation:
|
|
|
1271
1349
|
The ToOperation.
|
|
1272
1350
|
"""
|
|
1273
1351
|
return cls(
|
|
1274
|
-
_js_expr="", #
|
|
1275
|
-
_var_data=_var_data, #
|
|
1276
|
-
_var_type=_var_type or cls._default_var_type, #
|
|
1277
|
-
_original=value, #
|
|
1352
|
+
_js_expr="", # pyright: ignore [reportCallIssue]
|
|
1353
|
+
_var_data=_var_data, # pyright: ignore [reportCallIssue]
|
|
1354
|
+
_var_type=_var_type or cls._default_var_type, # pyright: ignore [reportCallIssue, reportAttributeAccessIssue]
|
|
1355
|
+
_original=value, # pyright: ignore [reportCallIssue]
|
|
1278
1356
|
)
|
|
1279
1357
|
|
|
1280
1358
|
|
|
@@ -1336,7 +1414,7 @@ class LiteralVar(Var):
|
|
|
1336
1414
|
_var_literal_subclasses.append((cls, var_subclass))
|
|
1337
1415
|
|
|
1338
1416
|
@classmethod
|
|
1339
|
-
def create(
|
|
1417
|
+
def create( # pyright: ignore [reportArgumentType]
|
|
1340
1418
|
cls,
|
|
1341
1419
|
value: Any,
|
|
1342
1420
|
_var_data: VarData | None = None,
|
|
@@ -1354,7 +1432,7 @@ class LiteralVar(Var):
|
|
|
1354
1432
|
TypeError: If the value is not a supported type for LiteralVar.
|
|
1355
1433
|
"""
|
|
1356
1434
|
from .object import LiteralObjectVar
|
|
1357
|
-
from .sequence import LiteralStringVar
|
|
1435
|
+
from .sequence import ArrayVar, LiteralStringVar
|
|
1358
1436
|
|
|
1359
1437
|
if isinstance(value, Var):
|
|
1360
1438
|
if _var_data is None:
|
|
@@ -1373,7 +1451,7 @@ class LiteralVar(Var):
|
|
|
1373
1451
|
|
|
1374
1452
|
serialized_value = serializers.serialize(value)
|
|
1375
1453
|
if serialized_value is not None:
|
|
1376
|
-
if isinstance(serialized_value,
|
|
1454
|
+
if isinstance(serialized_value, Mapping):
|
|
1377
1455
|
return LiteralObjectVar.create(
|
|
1378
1456
|
serialized_value,
|
|
1379
1457
|
_var_type=type(value),
|
|
@@ -1410,6 +1488,9 @@ class LiteralVar(Var):
|
|
|
1410
1488
|
_var_data=_var_data,
|
|
1411
1489
|
)
|
|
1412
1490
|
|
|
1491
|
+
if isinstance(value, range):
|
|
1492
|
+
return ArrayVar.range(value.start, value.stop, value.step)
|
|
1493
|
+
|
|
1413
1494
|
raise TypeError(
|
|
1414
1495
|
f"Unsupported type {type(value)} for LiteralVar. Tried to create a LiteralVar from {value}."
|
|
1415
1496
|
)
|
|
@@ -1417,6 +1498,12 @@ class LiteralVar(Var):
|
|
|
1417
1498
|
def __post_init__(self):
|
|
1418
1499
|
"""Post-initialize the var."""
|
|
1419
1500
|
|
|
1501
|
+
@property
|
|
1502
|
+
def _var_value(self) -> Any:
|
|
1503
|
+
raise NotImplementedError(
|
|
1504
|
+
"LiteralVar subclasses must implement the _var_value property."
|
|
1505
|
+
)
|
|
1506
|
+
|
|
1420
1507
|
def json(self) -> str:
|
|
1421
1508
|
"""Serialize the var to a JSON string.
|
|
1422
1509
|
|
|
@@ -1463,7 +1550,7 @@ T = TypeVar("T")
|
|
|
1463
1550
|
|
|
1464
1551
|
# NoReturn is used to match CustomVarOperationReturn with no type hint.
|
|
1465
1552
|
@overload
|
|
1466
|
-
def var_operation(
|
|
1553
|
+
def var_operation( # pyright: ignore [reportOverlappingOverload]
|
|
1467
1554
|
func: Callable[P, CustomVarOperationReturn[NoReturn]],
|
|
1468
1555
|
) -> Callable[P, Var]: ...
|
|
1469
1556
|
|
|
@@ -1489,7 +1576,7 @@ def var_operation(
|
|
|
1489
1576
|
) -> Callable[P, StringVar]: ...
|
|
1490
1577
|
|
|
1491
1578
|
|
|
1492
|
-
LIST_T = TypeVar("LIST_T", bound=
|
|
1579
|
+
LIST_T = TypeVar("LIST_T", bound=Sequence)
|
|
1493
1580
|
|
|
1494
1581
|
|
|
1495
1582
|
@overload
|
|
@@ -1498,7 +1585,7 @@ def var_operation(
|
|
|
1498
1585
|
) -> Callable[P, ArrayVar[LIST_T]]: ...
|
|
1499
1586
|
|
|
1500
1587
|
|
|
1501
|
-
OBJECT_TYPE = TypeVar("OBJECT_TYPE", bound=
|
|
1588
|
+
OBJECT_TYPE = TypeVar("OBJECT_TYPE", bound=Mapping)
|
|
1502
1589
|
|
|
1503
1590
|
|
|
1504
1591
|
@overload
|
|
@@ -1513,7 +1600,7 @@ def var_operation(
|
|
|
1513
1600
|
) -> Callable[P, Var[T]]: ...
|
|
1514
1601
|
|
|
1515
1602
|
|
|
1516
|
-
def var_operation(
|
|
1603
|
+
def var_operation( # pyright: ignore [reportInconsistentOverload]
|
|
1517
1604
|
func: Callable[P, CustomVarOperationReturn[T]],
|
|
1518
1605
|
) -> Callable[P, Var[T]]:
|
|
1519
1606
|
"""Decorator for creating a var operation.
|
|
@@ -1547,7 +1634,7 @@ def var_operation(
|
|
|
1547
1634
|
return CustomVarOperation.create(
|
|
1548
1635
|
name=func.__name__,
|
|
1549
1636
|
args=tuple(list(args_vars.items()) + list(kwargs_vars.items())),
|
|
1550
|
-
return_var=func(*args_vars.values(), **kwargs_vars), #
|
|
1637
|
+
return_var=func(*args_vars.values(), **kwargs_vars), # pyright: ignore [reportCallIssue, reportReturnType]
|
|
1551
1638
|
).guess_type()
|
|
1552
1639
|
|
|
1553
1640
|
return wrapper
|
|
@@ -1573,25 +1660,100 @@ def figure_out_type(value: Any) -> types.GenericType:
|
|
|
1573
1660
|
return Set[unionize(*(figure_out_type(v) for v in value))]
|
|
1574
1661
|
if isinstance(value, tuple):
|
|
1575
1662
|
return Tuple[unionize(*(figure_out_type(v) for v in value)), ...]
|
|
1576
|
-
if isinstance(value,
|
|
1577
|
-
return
|
|
1663
|
+
if isinstance(value, Mapping):
|
|
1664
|
+
return Mapping[
|
|
1578
1665
|
unionize(*(figure_out_type(k) for k in value)),
|
|
1579
1666
|
unionize(*(figure_out_type(v) for v in value.values())),
|
|
1580
1667
|
]
|
|
1581
1668
|
return type(value)
|
|
1582
1669
|
|
|
1583
1670
|
|
|
1584
|
-
|
|
1585
|
-
|
|
1671
|
+
GLOBAL_CACHE = {}
|
|
1672
|
+
|
|
1673
|
+
|
|
1674
|
+
class cached_property: # noqa: N801
|
|
1675
|
+
"""A cached property that caches the result of the function."""
|
|
1586
1676
|
|
|
1587
|
-
def __init__(self, func):
|
|
1588
|
-
"""Initialize the
|
|
1677
|
+
def __init__(self, func: Callable):
|
|
1678
|
+
"""Initialize the cached_property.
|
|
1589
1679
|
|
|
1590
1680
|
Args:
|
|
1591
1681
|
func: The function to cache.
|
|
1592
1682
|
"""
|
|
1593
|
-
|
|
1594
|
-
self.
|
|
1683
|
+
self._func = func
|
|
1684
|
+
self._attrname = None
|
|
1685
|
+
|
|
1686
|
+
def __set_name__(self, owner: Any, name: str):
|
|
1687
|
+
"""Set the name of the cached property.
|
|
1688
|
+
|
|
1689
|
+
Args:
|
|
1690
|
+
owner: The owner of the cached property.
|
|
1691
|
+
name: The name of the cached property.
|
|
1692
|
+
|
|
1693
|
+
Raises:
|
|
1694
|
+
TypeError: If the cached property is assigned to two different names.
|
|
1695
|
+
"""
|
|
1696
|
+
if self._attrname is None:
|
|
1697
|
+
self._attrname = name
|
|
1698
|
+
|
|
1699
|
+
original_del = getattr(owner, "__del__", None)
|
|
1700
|
+
|
|
1701
|
+
def delete_property(this: Any):
|
|
1702
|
+
"""Delete the cached property.
|
|
1703
|
+
|
|
1704
|
+
Args:
|
|
1705
|
+
this: The object to delete the cached property from.
|
|
1706
|
+
"""
|
|
1707
|
+
cached_field_name = "_reflex_cache_" + name
|
|
1708
|
+
try:
|
|
1709
|
+
unique_id = object.__getattribute__(this, cached_field_name)
|
|
1710
|
+
except AttributeError:
|
|
1711
|
+
if original_del is not None:
|
|
1712
|
+
original_del(this)
|
|
1713
|
+
return
|
|
1714
|
+
if unique_id in GLOBAL_CACHE:
|
|
1715
|
+
del GLOBAL_CACHE[unique_id]
|
|
1716
|
+
|
|
1717
|
+
if original_del is not None:
|
|
1718
|
+
original_del(this)
|
|
1719
|
+
|
|
1720
|
+
owner.__del__ = delete_property
|
|
1721
|
+
|
|
1722
|
+
elif name != self._attrname:
|
|
1723
|
+
raise TypeError(
|
|
1724
|
+
"Cannot assign the same cached_property to two different names "
|
|
1725
|
+
f"({self._attrname!r} and {name!r})."
|
|
1726
|
+
)
|
|
1727
|
+
|
|
1728
|
+
def __get__(self, instance: Any, owner: Type | None = None):
|
|
1729
|
+
"""Get the cached property.
|
|
1730
|
+
|
|
1731
|
+
Args:
|
|
1732
|
+
instance: The instance to get the cached property from.
|
|
1733
|
+
owner: The owner of the cached property.
|
|
1734
|
+
|
|
1735
|
+
Returns:
|
|
1736
|
+
The cached property.
|
|
1737
|
+
|
|
1738
|
+
Raises:
|
|
1739
|
+
TypeError: If the class does not have __set_name__.
|
|
1740
|
+
"""
|
|
1741
|
+
if self._attrname is None:
|
|
1742
|
+
raise TypeError(
|
|
1743
|
+
"Cannot use cached_property on a class without __set_name__."
|
|
1744
|
+
)
|
|
1745
|
+
cached_field_name = "_reflex_cache_" + self._attrname
|
|
1746
|
+
try:
|
|
1747
|
+
unique_id = object.__getattribute__(instance, cached_field_name)
|
|
1748
|
+
except AttributeError:
|
|
1749
|
+
unique_id = uuid.uuid4().int
|
|
1750
|
+
object.__setattr__(instance, cached_field_name, unique_id)
|
|
1751
|
+
if unique_id not in GLOBAL_CACHE:
|
|
1752
|
+
GLOBAL_CACHE[unique_id] = self._func(instance)
|
|
1753
|
+
return GLOBAL_CACHE[unique_id]
|
|
1754
|
+
|
|
1755
|
+
|
|
1756
|
+
cached_property_no_lock = cached_property
|
|
1595
1757
|
|
|
1596
1758
|
|
|
1597
1759
|
class CachedVarOperation:
|
|
@@ -1617,7 +1779,7 @@ class CachedVarOperation:
|
|
|
1617
1779
|
|
|
1618
1780
|
next_class = parent_classes[parent_classes.index(CachedVarOperation) + 1]
|
|
1619
1781
|
|
|
1620
|
-
return next_class.__getattr__(self, name)
|
|
1782
|
+
return next_class.__getattr__(self, name)
|
|
1621
1783
|
|
|
1622
1784
|
def _get_all_var_data(self) -> VarData | None:
|
|
1623
1785
|
"""Get all VarData associated with the Var.
|
|
@@ -1639,7 +1801,7 @@ class CachedVarOperation:
|
|
|
1639
1801
|
value._get_all_var_data() if isinstance(value, Var) else None
|
|
1640
1802
|
for value in (
|
|
1641
1803
|
getattr(self, field.name)
|
|
1642
|
-
for field in dataclasses.fields(self) #
|
|
1804
|
+
for field in dataclasses.fields(self) # pyright: ignore [reportArgumentType]
|
|
1643
1805
|
)
|
|
1644
1806
|
),
|
|
1645
1807
|
self._var_data,
|
|
@@ -1656,7 +1818,7 @@ class CachedVarOperation:
|
|
|
1656
1818
|
type(self).__name__,
|
|
1657
1819
|
*[
|
|
1658
1820
|
getattr(self, field.name)
|
|
1659
|
-
for field in dataclasses.fields(self) #
|
|
1821
|
+
for field in dataclasses.fields(self) # pyright: ignore [reportArgumentType]
|
|
1660
1822
|
if field.name not in ["_js_expr", "_var_data", "_var_type"]
|
|
1661
1823
|
],
|
|
1662
1824
|
)
|
|
@@ -1673,7 +1835,7 @@ def and_operation(a: Var | Any, b: Var | Any) -> Var:
|
|
|
1673
1835
|
Returns:
|
|
1674
1836
|
The result of the logical AND operation.
|
|
1675
1837
|
"""
|
|
1676
|
-
return _and_operation(a, b)
|
|
1838
|
+
return _and_operation(a, b)
|
|
1677
1839
|
|
|
1678
1840
|
|
|
1679
1841
|
@var_operation
|
|
@@ -1703,7 +1865,7 @@ def or_operation(a: Var | Any, b: Var | Any) -> Var:
|
|
|
1703
1865
|
Returns:
|
|
1704
1866
|
The result of the logical OR operation.
|
|
1705
1867
|
"""
|
|
1706
|
-
return _or_operation(a, b)
|
|
1868
|
+
return _or_operation(a, b)
|
|
1707
1869
|
|
|
1708
1870
|
|
|
1709
1871
|
@var_operation
|
|
@@ -1726,7 +1888,7 @@ def _or_operation(a: Var, b: Var):
|
|
|
1726
1888
|
@dataclasses.dataclass(
|
|
1727
1889
|
eq=False,
|
|
1728
1890
|
frozen=True,
|
|
1729
|
-
|
|
1891
|
+
slots=True,
|
|
1730
1892
|
)
|
|
1731
1893
|
class CallableVar(Var):
|
|
1732
1894
|
"""Decorate a Var-returning function to act as both a Var and a function.
|
|
@@ -1757,7 +1919,7 @@ class CallableVar(Var):
|
|
|
1757
1919
|
object.__setattr__(self, "fn", fn)
|
|
1758
1920
|
object.__setattr__(self, "original_var", original_var)
|
|
1759
1921
|
|
|
1760
|
-
def __call__(self, *args, **kwargs) -> Var:
|
|
1922
|
+
def __call__(self, *args: Any, **kwargs: Any) -> Var:
|
|
1761
1923
|
"""Call the decorated function.
|
|
1762
1924
|
|
|
1763
1925
|
Args:
|
|
@@ -1807,7 +1969,7 @@ def is_computed_var(obj: Any) -> TypeGuard[ComputedVar]:
|
|
|
1807
1969
|
@dataclasses.dataclass(
|
|
1808
1970
|
eq=False,
|
|
1809
1971
|
frozen=True,
|
|
1810
|
-
|
|
1972
|
+
slots=True,
|
|
1811
1973
|
)
|
|
1812
1974
|
class ComputedVar(Var[RETURN_TYPE]):
|
|
1813
1975
|
"""A field with computed getters."""
|
|
@@ -1822,7 +1984,7 @@ class ComputedVar(Var[RETURN_TYPE]):
|
|
|
1822
1984
|
_initial_value: RETURN_TYPE | types.Unset = dataclasses.field(default=types.Unset())
|
|
1823
1985
|
|
|
1824
1986
|
# Explicit var dependencies to track
|
|
1825
|
-
_static_deps: set[str] = dataclasses.field(default_factory=
|
|
1987
|
+
_static_deps: dict[str | None, set[str]] = dataclasses.field(default_factory=dict)
|
|
1826
1988
|
|
|
1827
1989
|
# Whether var dependencies should be auto-determined
|
|
1828
1990
|
_auto_deps: bool = dataclasses.field(default=True)
|
|
@@ -1832,13 +1994,13 @@ class ComputedVar(Var[RETURN_TYPE]):
|
|
|
1832
1994
|
|
|
1833
1995
|
_fget: Callable[[BaseState], RETURN_TYPE] = dataclasses.field(
|
|
1834
1996
|
default_factory=lambda: lambda _: None
|
|
1835
|
-
) #
|
|
1997
|
+
) # pyright: ignore [reportAssignmentType]
|
|
1836
1998
|
|
|
1837
1999
|
def __init__(
|
|
1838
2000
|
self,
|
|
1839
2001
|
fget: Callable[[BASE_STATE], RETURN_TYPE],
|
|
1840
2002
|
initial_value: RETURN_TYPE | types.Unset = types.Unset(),
|
|
1841
|
-
cache: bool =
|
|
2003
|
+
cache: bool = True,
|
|
1842
2004
|
deps: Optional[List[Union[str, Var]]] = None,
|
|
1843
2005
|
auto_deps: bool = True,
|
|
1844
2006
|
interval: Optional[Union[int, datetime.timedelta]] = None,
|
|
@@ -1859,19 +2021,14 @@ class ComputedVar(Var[RETURN_TYPE]):
|
|
|
1859
2021
|
|
|
1860
2022
|
Raises:
|
|
1861
2023
|
TypeError: If the computed var dependencies are not Var instances or var names.
|
|
2024
|
+
UntypedComputedVarError: If the computed var is untyped.
|
|
1862
2025
|
"""
|
|
1863
2026
|
hint = kwargs.pop("return_type", None) or get_type_hints(fget).get(
|
|
1864
2027
|
"return", Any
|
|
1865
2028
|
)
|
|
1866
2029
|
|
|
1867
2030
|
if hint is Any:
|
|
1868
|
-
|
|
1869
|
-
"untyped-computed-var",
|
|
1870
|
-
"ComputedVar should have a return type annotation.",
|
|
1871
|
-
"0.6.5",
|
|
1872
|
-
"0.7.0",
|
|
1873
|
-
)
|
|
1874
|
-
|
|
2031
|
+
raise UntypedComputedVarError(var_name=fget.__name__)
|
|
1875
2032
|
kwargs.setdefault("_js_expr", fget.__name__)
|
|
1876
2033
|
kwargs.setdefault("_var_type", hint)
|
|
1877
2034
|
|
|
@@ -1897,28 +2054,78 @@ class ComputedVar(Var[RETURN_TYPE]):
|
|
|
1897
2054
|
|
|
1898
2055
|
object.__setattr__(self, "_update_interval", interval)
|
|
1899
2056
|
|
|
1900
|
-
if deps is None:
|
|
1901
|
-
deps = []
|
|
1902
|
-
else:
|
|
1903
|
-
for dep in deps:
|
|
1904
|
-
if isinstance(dep, Var):
|
|
1905
|
-
continue
|
|
1906
|
-
if isinstance(dep, str) and dep != "":
|
|
1907
|
-
continue
|
|
1908
|
-
raise TypeError(
|
|
1909
|
-
"ComputedVar dependencies must be Var instances or var names (non-empty strings)."
|
|
1910
|
-
)
|
|
1911
2057
|
object.__setattr__(
|
|
1912
2058
|
self,
|
|
1913
2059
|
"_static_deps",
|
|
1914
|
-
|
|
2060
|
+
self._calculate_static_deps(deps),
|
|
1915
2061
|
)
|
|
1916
2062
|
object.__setattr__(self, "_auto_deps", auto_deps)
|
|
1917
2063
|
|
|
1918
2064
|
object.__setattr__(self, "_fget", fget)
|
|
1919
2065
|
|
|
2066
|
+
def _calculate_static_deps(
|
|
2067
|
+
self,
|
|
2068
|
+
deps: Union[List[Union[str, Var]], dict[str | None, set[str]]] | None = None,
|
|
2069
|
+
) -> dict[str | None, set[str]]:
|
|
2070
|
+
"""Calculate the static dependencies of the computed var from user input or existing dependencies.
|
|
2071
|
+
|
|
2072
|
+
Args:
|
|
2073
|
+
deps: The user input dependencies or existing dependencies.
|
|
2074
|
+
|
|
2075
|
+
Returns:
|
|
2076
|
+
The static dependencies.
|
|
2077
|
+
"""
|
|
2078
|
+
if isinstance(deps, dict):
|
|
2079
|
+
# Assume a dict is coming from _replace, so no special processing.
|
|
2080
|
+
return deps
|
|
2081
|
+
_static_deps = {}
|
|
2082
|
+
if deps is not None:
|
|
2083
|
+
for dep in deps:
|
|
2084
|
+
_static_deps = self._add_static_dep(dep, _static_deps)
|
|
2085
|
+
return _static_deps
|
|
2086
|
+
|
|
2087
|
+
def _add_static_dep(
|
|
2088
|
+
self, dep: Union[str, Var], deps: dict[str | None, set[str]] | None = None
|
|
2089
|
+
) -> dict[str | None, set[str]]:
|
|
2090
|
+
"""Add a static dependency to the computed var or existing dependency set.
|
|
2091
|
+
|
|
2092
|
+
Args:
|
|
2093
|
+
dep: The dependency to add.
|
|
2094
|
+
deps: The existing dependency set.
|
|
2095
|
+
|
|
2096
|
+
Returns:
|
|
2097
|
+
The updated dependency set.
|
|
2098
|
+
|
|
2099
|
+
Raises:
|
|
2100
|
+
TypeError: If the computed var dependencies are not Var instances or var names.
|
|
2101
|
+
"""
|
|
2102
|
+
if deps is None:
|
|
2103
|
+
deps = self._static_deps
|
|
2104
|
+
if isinstance(dep, Var):
|
|
2105
|
+
state_name = (
|
|
2106
|
+
all_var_data.state
|
|
2107
|
+
if (all_var_data := dep._get_all_var_data()) and all_var_data.state
|
|
2108
|
+
else None
|
|
2109
|
+
)
|
|
2110
|
+
if all_var_data is not None:
|
|
2111
|
+
var_name = all_var_data.field_name
|
|
2112
|
+
else:
|
|
2113
|
+
var_name = dep._js_expr
|
|
2114
|
+
deps.setdefault(state_name, set()).add(var_name)
|
|
2115
|
+
elif isinstance(dep, str) and dep != "":
|
|
2116
|
+
deps.setdefault(None, set()).add(dep)
|
|
2117
|
+
else:
|
|
2118
|
+
raise TypeError(
|
|
2119
|
+
"ComputedVar dependencies must be Var instances or var names (non-empty strings)."
|
|
2120
|
+
)
|
|
2121
|
+
return deps
|
|
2122
|
+
|
|
1920
2123
|
@override
|
|
1921
|
-
def _replace(
|
|
2124
|
+
def _replace(
|
|
2125
|
+
self,
|
|
2126
|
+
merge_var_data: VarData | None = None,
|
|
2127
|
+
**kwargs: Any,
|
|
2128
|
+
) -> Self:
|
|
1922
2129
|
"""Replace the attributes of the ComputedVar.
|
|
1923
2130
|
|
|
1924
2131
|
Args:
|
|
@@ -1931,6 +2138,8 @@ class ComputedVar(Var[RETURN_TYPE]):
|
|
|
1931
2138
|
Raises:
|
|
1932
2139
|
TypeError: If kwargs contains keys that are not allowed.
|
|
1933
2140
|
"""
|
|
2141
|
+
if "deps" in kwargs:
|
|
2142
|
+
kwargs["deps"] = self._calculate_static_deps(kwargs["deps"])
|
|
1934
2143
|
field_values = {
|
|
1935
2144
|
"fget": kwargs.pop("fget", self._fget),
|
|
1936
2145
|
"initial_value": kwargs.pop("initial_value", self._initial_value),
|
|
@@ -1944,6 +2153,7 @@ class ComputedVar(Var[RETURN_TYPE]):
|
|
|
1944
2153
|
"_var_data": kwargs.pop(
|
|
1945
2154
|
"_var_data", VarData.merge(self._var_data, merge_var_data)
|
|
1946
2155
|
),
|
|
2156
|
+
"return_type": kwargs.pop("return_type", self._var_type),
|
|
1947
2157
|
}
|
|
1948
2158
|
|
|
1949
2159
|
if kwargs:
|
|
@@ -1986,6 +2196,13 @@ class ComputedVar(Var[RETURN_TYPE]):
|
|
|
1986
2196
|
return True
|
|
1987
2197
|
return datetime.datetime.now() - last_updated > self._update_interval
|
|
1988
2198
|
|
|
2199
|
+
@overload
|
|
2200
|
+
def __get__(
|
|
2201
|
+
self: ComputedVar[bool],
|
|
2202
|
+
instance: None,
|
|
2203
|
+
owner: Type,
|
|
2204
|
+
) -> BooleanVar: ...
|
|
2205
|
+
|
|
1989
2206
|
@overload
|
|
1990
2207
|
def __get__(
|
|
1991
2208
|
self: ComputedVar[int] | ComputedVar[float],
|
|
@@ -2002,10 +2219,10 @@ class ComputedVar(Var[RETURN_TYPE]):
|
|
|
2002
2219
|
|
|
2003
2220
|
@overload
|
|
2004
2221
|
def __get__(
|
|
2005
|
-
self: ComputedVar[
|
|
2222
|
+
self: ComputedVar[Mapping[DICT_KEY, DICT_VAL]],
|
|
2006
2223
|
instance: None,
|
|
2007
2224
|
owner: Type,
|
|
2008
|
-
) -> ObjectVar[
|
|
2225
|
+
) -> ObjectVar[Mapping[DICT_KEY, DICT_VAL]]: ...
|
|
2009
2226
|
|
|
2010
2227
|
@overload
|
|
2011
2228
|
def __get__(
|
|
@@ -2014,13 +2231,6 @@ class ComputedVar(Var[RETURN_TYPE]):
|
|
|
2014
2231
|
owner: Type,
|
|
2015
2232
|
) -> ArrayVar[list[LIST_INSIDE]]: ...
|
|
2016
2233
|
|
|
2017
|
-
@overload
|
|
2018
|
-
def __get__(
|
|
2019
|
-
self: ComputedVar[set[LIST_INSIDE]],
|
|
2020
|
-
instance: None,
|
|
2021
|
-
owner: Type,
|
|
2022
|
-
) -> ArrayVar[set[LIST_INSIDE]]: ...
|
|
2023
|
-
|
|
2024
2234
|
@overload
|
|
2025
2235
|
def __get__(
|
|
2026
2236
|
self: ComputedVar[tuple[LIST_INSIDE, ...]],
|
|
@@ -2034,7 +2244,7 @@ class ComputedVar(Var[RETURN_TYPE]):
|
|
|
2034
2244
|
@overload
|
|
2035
2245
|
def __get__(self, instance: BaseState, owner: Type) -> RETURN_TYPE: ...
|
|
2036
2246
|
|
|
2037
|
-
def __get__(self, instance: BaseState | None, owner):
|
|
2247
|
+
def __get__(self, instance: BaseState | None, owner: Type):
|
|
2038
2248
|
"""Get the ComputedVar value.
|
|
2039
2249
|
|
|
2040
2250
|
If the value is already cached on the instance, return the cached value.
|
|
@@ -2077,130 +2287,69 @@ class ComputedVar(Var[RETURN_TYPE]):
|
|
|
2077
2287
|
setattr(instance, self._last_updated_attr, datetime.datetime.now())
|
|
2078
2288
|
value = getattr(instance, self._cache_attr)
|
|
2079
2289
|
|
|
2080
|
-
|
|
2081
|
-
console.deprecate(
|
|
2082
|
-
"mismatched-computed-var-return",
|
|
2083
|
-
f"Computed var {type(instance).__name__}.{self._js_expr} returned value of type {type(value)}, "
|
|
2084
|
-
f"expected {self._var_type}. This might cause unexpected behavior.",
|
|
2085
|
-
"0.6.5",
|
|
2086
|
-
"0.7.0",
|
|
2087
|
-
)
|
|
2290
|
+
self._check_deprecated_return_type(instance, value)
|
|
2088
2291
|
|
|
2089
2292
|
return value
|
|
2090
2293
|
|
|
2294
|
+
def _check_deprecated_return_type(self, instance: BaseState, value: Any) -> None:
|
|
2295
|
+
if not _isinstance(value, self._var_type):
|
|
2296
|
+
console.error(
|
|
2297
|
+
f"Computed var '{type(instance).__name__}.{self._js_expr}' must return"
|
|
2298
|
+
f" type '{self._var_type}', got '{type(value)}'."
|
|
2299
|
+
)
|
|
2300
|
+
|
|
2091
2301
|
def _deps(
|
|
2092
2302
|
self,
|
|
2093
|
-
objclass: Type,
|
|
2303
|
+
objclass: Type[BaseState],
|
|
2094
2304
|
obj: FunctionType | CodeType | None = None,
|
|
2095
|
-
|
|
2096
|
-
) -> set[str]:
|
|
2305
|
+
) -> dict[str, set[str]]:
|
|
2097
2306
|
"""Determine var dependencies of this ComputedVar.
|
|
2098
2307
|
|
|
2099
|
-
Save references to attributes accessed on "self".
|
|
2100
|
-
|
|
2101
|
-
|
|
2308
|
+
Save references to attributes accessed on "self" or other fetched states.
|
|
2309
|
+
|
|
2310
|
+
Recursively called when the function makes a method call on "self" or
|
|
2311
|
+
define comprehensions or nested functions that may reference "self".
|
|
2102
2312
|
|
|
2103
2313
|
Args:
|
|
2104
2314
|
objclass: the class obj this ComputedVar is attached to.
|
|
2105
2315
|
obj: the object to disassemble (defaults to the fget function).
|
|
2106
|
-
self_name: if specified, look for this name in LOAD_FAST and LOAD_DEREF instructions.
|
|
2107
2316
|
|
|
2108
2317
|
Returns:
|
|
2109
|
-
A
|
|
2110
|
-
|
|
2111
|
-
Raises:
|
|
2112
|
-
VarValueError: if the function references the get_state, parent_state, or substates attributes
|
|
2113
|
-
(cannot track deps in a related state, only implicitly via parent state).
|
|
2318
|
+
A dictionary mapping state names to the set of variable names
|
|
2319
|
+
accessed by the given obj.
|
|
2114
2320
|
"""
|
|
2321
|
+
from .dep_tracking import DependencyTracker
|
|
2322
|
+
|
|
2323
|
+
d = {}
|
|
2324
|
+
if self._static_deps:
|
|
2325
|
+
d.update(self._static_deps)
|
|
2326
|
+
# None is a placeholder for the current state class.
|
|
2327
|
+
if None in d:
|
|
2328
|
+
d[objclass.get_full_name()] = d.pop(None)
|
|
2329
|
+
|
|
2115
2330
|
if not self._auto_deps:
|
|
2116
|
-
return
|
|
2117
|
-
|
|
2331
|
+
return d
|
|
2332
|
+
|
|
2118
2333
|
if obj is None:
|
|
2119
2334
|
fget = self._fget
|
|
2120
2335
|
if fget is not None:
|
|
2121
2336
|
obj = cast(FunctionType, fget)
|
|
2122
2337
|
else:
|
|
2123
|
-
return
|
|
2124
|
-
with contextlib.suppress(AttributeError):
|
|
2125
|
-
# unbox functools.partial
|
|
2126
|
-
obj = cast(FunctionType, obj.func) # type: ignore
|
|
2127
|
-
with contextlib.suppress(AttributeError):
|
|
2128
|
-
# unbox EventHandler
|
|
2129
|
-
obj = cast(FunctionType, obj.fn) # type: ignore
|
|
2338
|
+
return d
|
|
2130
2339
|
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
self_is_top_of_stack = False
|
|
2143
|
-
for instruction in dis.get_instructions(obj):
|
|
2144
|
-
if (
|
|
2145
|
-
instruction.opname in ("LOAD_FAST", "LOAD_DEREF")
|
|
2146
|
-
and instruction.argval == self_name
|
|
2147
|
-
):
|
|
2148
|
-
# bytecode loaded the class instance to the top of stack, next load instruction
|
|
2149
|
-
# is referencing an attribute on self
|
|
2150
|
-
self_is_top_of_stack = True
|
|
2151
|
-
continue
|
|
2152
|
-
if self_is_top_of_stack and instruction.opname in (
|
|
2153
|
-
"LOAD_ATTR",
|
|
2154
|
-
"LOAD_METHOD",
|
|
2155
|
-
):
|
|
2156
|
-
try:
|
|
2157
|
-
ref_obj = getattr(objclass, instruction.argval)
|
|
2158
|
-
except Exception:
|
|
2159
|
-
ref_obj = None
|
|
2160
|
-
if instruction.argval in invalid_names:
|
|
2161
|
-
raise VarValueError(
|
|
2162
|
-
f"Cached var {self!s} cannot access arbitrary state via `{instruction.argval}`."
|
|
2163
|
-
)
|
|
2164
|
-
if callable(ref_obj):
|
|
2165
|
-
# recurse into callable attributes
|
|
2166
|
-
d.update(
|
|
2167
|
-
self._deps(
|
|
2168
|
-
objclass=objclass,
|
|
2169
|
-
obj=ref_obj,
|
|
2170
|
-
)
|
|
2171
|
-
)
|
|
2172
|
-
# recurse into property fget functions
|
|
2173
|
-
elif isinstance(ref_obj, property) and not isinstance(
|
|
2174
|
-
ref_obj, ComputedVar
|
|
2175
|
-
):
|
|
2176
|
-
d.update(
|
|
2177
|
-
self._deps(
|
|
2178
|
-
objclass=objclass,
|
|
2179
|
-
obj=ref_obj.fget, # type: ignore
|
|
2180
|
-
)
|
|
2181
|
-
)
|
|
2182
|
-
elif (
|
|
2183
|
-
instruction.argval in objclass.backend_vars
|
|
2184
|
-
or instruction.argval in objclass.vars
|
|
2185
|
-
):
|
|
2186
|
-
# var access
|
|
2187
|
-
d.add(instruction.argval)
|
|
2188
|
-
elif instruction.opname == "LOAD_CONST" and isinstance(
|
|
2189
|
-
instruction.argval, CodeType
|
|
2190
|
-
):
|
|
2191
|
-
# recurse into nested functions / comprehensions, which can reference
|
|
2192
|
-
# instance attributes from the outer scope
|
|
2193
|
-
d.update(
|
|
2194
|
-
self._deps(
|
|
2195
|
-
objclass=objclass,
|
|
2196
|
-
obj=instruction.argval,
|
|
2197
|
-
self_name=self_name,
|
|
2198
|
-
)
|
|
2199
|
-
)
|
|
2200
|
-
self_is_top_of_stack = False
|
|
2201
|
-
return d
|
|
2340
|
+
try:
|
|
2341
|
+
return DependencyTracker(
|
|
2342
|
+
func=obj, state_cls=objclass, dependencies=d
|
|
2343
|
+
).dependencies
|
|
2344
|
+
except Exception as e:
|
|
2345
|
+
console.warn(
|
|
2346
|
+
"Failed to automatically determine dependencies for computed var "
|
|
2347
|
+
f"{objclass.__name__}.{self._js_expr}: {e}. "
|
|
2348
|
+
"Provide static_deps and set auto_deps=False to suppress this warning."
|
|
2349
|
+
)
|
|
2350
|
+
return d
|
|
2202
2351
|
|
|
2203
|
-
def mark_dirty(self, instance) -> None:
|
|
2352
|
+
def mark_dirty(self, instance: BaseState) -> None:
|
|
2204
2353
|
"""Mark this ComputedVar as dirty.
|
|
2205
2354
|
|
|
2206
2355
|
Args:
|
|
@@ -2209,6 +2358,37 @@ class ComputedVar(Var[RETURN_TYPE]):
|
|
|
2209
2358
|
with contextlib.suppress(AttributeError):
|
|
2210
2359
|
delattr(instance, self._cache_attr)
|
|
2211
2360
|
|
|
2361
|
+
def add_dependency(self, objclass: Type[BaseState], dep: Var):
|
|
2362
|
+
"""Explicitly add a dependency to the ComputedVar.
|
|
2363
|
+
|
|
2364
|
+
After adding the dependency, when the `dep` changes, this computed var
|
|
2365
|
+
will be marked dirty.
|
|
2366
|
+
|
|
2367
|
+
Args:
|
|
2368
|
+
objclass: The class obj this ComputedVar is attached to.
|
|
2369
|
+
dep: The dependency to add.
|
|
2370
|
+
|
|
2371
|
+
Raises:
|
|
2372
|
+
VarDependencyError: If the dependency is not a Var instance with a
|
|
2373
|
+
state and field name
|
|
2374
|
+
"""
|
|
2375
|
+
if all_var_data := dep._get_all_var_data():
|
|
2376
|
+
state_name = all_var_data.state
|
|
2377
|
+
if state_name:
|
|
2378
|
+
var_name = all_var_data.field_name
|
|
2379
|
+
if var_name:
|
|
2380
|
+
self._static_deps.setdefault(state_name, set()).add(var_name)
|
|
2381
|
+
objclass.get_root_state().get_class_substate(
|
|
2382
|
+
state_name
|
|
2383
|
+
)._var_dependencies.setdefault(var_name, set()).add(
|
|
2384
|
+
(objclass.get_full_name(), self._js_expr)
|
|
2385
|
+
)
|
|
2386
|
+
return
|
|
2387
|
+
raise VarDependencyError(
|
|
2388
|
+
"ComputedVar dependencies must be Var instances with a state and "
|
|
2389
|
+
f"field name, got {dep!r}."
|
|
2390
|
+
)
|
|
2391
|
+
|
|
2212
2392
|
def _determine_var_type(self) -> Type:
|
|
2213
2393
|
"""Get the type of the var.
|
|
2214
2394
|
|
|
@@ -2218,7 +2398,7 @@ class ComputedVar(Var[RETURN_TYPE]):
|
|
|
2218
2398
|
hints = get_type_hints(self._fget)
|
|
2219
2399
|
if "return" in hints:
|
|
2220
2400
|
return hints["return"]
|
|
2221
|
-
return Any
|
|
2401
|
+
return Any # pyright: ignore [reportReturnType]
|
|
2222
2402
|
|
|
2223
2403
|
@property
|
|
2224
2404
|
def __class__(self) -> Type:
|
|
@@ -2245,6 +2425,126 @@ class DynamicRouteVar(ComputedVar[Union[str, List[str]]]):
|
|
|
2245
2425
|
pass
|
|
2246
2426
|
|
|
2247
2427
|
|
|
2428
|
+
async def _default_async_computed_var(_self: BaseState) -> Any:
|
|
2429
|
+
return None
|
|
2430
|
+
|
|
2431
|
+
|
|
2432
|
+
@dataclasses.dataclass(
|
|
2433
|
+
eq=False,
|
|
2434
|
+
frozen=True,
|
|
2435
|
+
init=False,
|
|
2436
|
+
slots=True,
|
|
2437
|
+
)
|
|
2438
|
+
class AsyncComputedVar(ComputedVar[RETURN_TYPE]):
|
|
2439
|
+
"""A computed var that wraps a coroutinefunction."""
|
|
2440
|
+
|
|
2441
|
+
_fget: Callable[[BaseState], Coroutine[None, None, RETURN_TYPE]] = (
|
|
2442
|
+
dataclasses.field(default=_default_async_computed_var)
|
|
2443
|
+
)
|
|
2444
|
+
|
|
2445
|
+
@overload
|
|
2446
|
+
def __get__(
|
|
2447
|
+
self: AsyncComputedVar[bool],
|
|
2448
|
+
instance: None,
|
|
2449
|
+
owner: Type,
|
|
2450
|
+
) -> BooleanVar: ...
|
|
2451
|
+
|
|
2452
|
+
@overload
|
|
2453
|
+
def __get__(
|
|
2454
|
+
self: AsyncComputedVar[int] | ComputedVar[float],
|
|
2455
|
+
instance: None,
|
|
2456
|
+
owner: Type,
|
|
2457
|
+
) -> NumberVar: ...
|
|
2458
|
+
|
|
2459
|
+
@overload
|
|
2460
|
+
def __get__(
|
|
2461
|
+
self: AsyncComputedVar[str],
|
|
2462
|
+
instance: None,
|
|
2463
|
+
owner: Type,
|
|
2464
|
+
) -> StringVar: ...
|
|
2465
|
+
|
|
2466
|
+
@overload
|
|
2467
|
+
def __get__(
|
|
2468
|
+
self: AsyncComputedVar[Mapping[DICT_KEY, DICT_VAL]],
|
|
2469
|
+
instance: None,
|
|
2470
|
+
owner: Type,
|
|
2471
|
+
) -> ObjectVar[Mapping[DICT_KEY, DICT_VAL]]: ...
|
|
2472
|
+
|
|
2473
|
+
@overload
|
|
2474
|
+
def __get__(
|
|
2475
|
+
self: AsyncComputedVar[list[LIST_INSIDE]],
|
|
2476
|
+
instance: None,
|
|
2477
|
+
owner: Type,
|
|
2478
|
+
) -> ArrayVar[list[LIST_INSIDE]]: ...
|
|
2479
|
+
|
|
2480
|
+
@overload
|
|
2481
|
+
def __get__(
|
|
2482
|
+
self: AsyncComputedVar[tuple[LIST_INSIDE, ...]],
|
|
2483
|
+
instance: None,
|
|
2484
|
+
owner: Type,
|
|
2485
|
+
) -> ArrayVar[tuple[LIST_INSIDE, ...]]: ...
|
|
2486
|
+
|
|
2487
|
+
@overload
|
|
2488
|
+
def __get__(self, instance: None, owner: Type) -> AsyncComputedVar[RETURN_TYPE]: ...
|
|
2489
|
+
|
|
2490
|
+
@overload
|
|
2491
|
+
def __get__(
|
|
2492
|
+
self, instance: BaseState, owner: Type
|
|
2493
|
+
) -> Coroutine[None, None, RETURN_TYPE]: ...
|
|
2494
|
+
|
|
2495
|
+
def __get__(
|
|
2496
|
+
self, instance: BaseState | None, owner
|
|
2497
|
+
) -> Var | Coroutine[None, None, RETURN_TYPE]:
|
|
2498
|
+
"""Get the ComputedVar value.
|
|
2499
|
+
|
|
2500
|
+
If the value is already cached on the instance, return the cached value.
|
|
2501
|
+
|
|
2502
|
+
Args:
|
|
2503
|
+
instance: the instance of the class accessing this computed var.
|
|
2504
|
+
owner: the class that this descriptor is attached to.
|
|
2505
|
+
|
|
2506
|
+
Returns:
|
|
2507
|
+
The value of the var for the given instance.
|
|
2508
|
+
"""
|
|
2509
|
+
if instance is None:
|
|
2510
|
+
return super(AsyncComputedVar, self).__get__(instance, owner)
|
|
2511
|
+
|
|
2512
|
+
if not self._cache:
|
|
2513
|
+
|
|
2514
|
+
async def _awaitable_result(instance: BaseState = instance) -> RETURN_TYPE:
|
|
2515
|
+
value = await self.fget(instance)
|
|
2516
|
+
self._check_deprecated_return_type(instance, value)
|
|
2517
|
+
return value
|
|
2518
|
+
|
|
2519
|
+
return _awaitable_result()
|
|
2520
|
+
else:
|
|
2521
|
+
# handle caching
|
|
2522
|
+
async def _awaitable_result(instance: BaseState = instance) -> RETURN_TYPE:
|
|
2523
|
+
if not hasattr(instance, self._cache_attr) or self.needs_update(
|
|
2524
|
+
instance
|
|
2525
|
+
):
|
|
2526
|
+
# Set cache attr on state instance.
|
|
2527
|
+
setattr(instance, self._cache_attr, await self.fget(instance))
|
|
2528
|
+
# Ensure the computed var gets serialized to redis.
|
|
2529
|
+
instance._was_touched = True
|
|
2530
|
+
# Set the last updated timestamp on the state instance.
|
|
2531
|
+
setattr(instance, self._last_updated_attr, datetime.datetime.now())
|
|
2532
|
+
value = getattr(instance, self._cache_attr)
|
|
2533
|
+
self._check_deprecated_return_type(instance, value)
|
|
2534
|
+
return value
|
|
2535
|
+
|
|
2536
|
+
return _awaitable_result()
|
|
2537
|
+
|
|
2538
|
+
@property
|
|
2539
|
+
def fget(self) -> Callable[[BaseState], Coroutine[None, None, RETURN_TYPE]]:
|
|
2540
|
+
"""Get the getter function.
|
|
2541
|
+
|
|
2542
|
+
Returns:
|
|
2543
|
+
The getter function.
|
|
2544
|
+
"""
|
|
2545
|
+
return self._fget
|
|
2546
|
+
|
|
2547
|
+
|
|
2248
2548
|
if TYPE_CHECKING:
|
|
2249
2549
|
BASE_STATE = TypeVar("BASE_STATE", bound=BaseState)
|
|
2250
2550
|
|
|
@@ -2253,20 +2553,20 @@ if TYPE_CHECKING:
|
|
|
2253
2553
|
def computed_var(
|
|
2254
2554
|
fget: None = None,
|
|
2255
2555
|
initial_value: Any | types.Unset = types.Unset(),
|
|
2256
|
-
cache: bool =
|
|
2556
|
+
cache: bool = True,
|
|
2257
2557
|
deps: Optional[List[Union[str, Var]]] = None,
|
|
2258
2558
|
auto_deps: bool = True,
|
|
2259
2559
|
interval: Optional[Union[datetime.timedelta, int]] = None,
|
|
2260
2560
|
backend: bool | None = None,
|
|
2261
2561
|
**kwargs,
|
|
2262
|
-
) -> Callable[[Callable[[BASE_STATE], RETURN_TYPE]], ComputedVar[RETURN_TYPE]]: ...
|
|
2562
|
+
) -> Callable[[Callable[[BASE_STATE], RETURN_TYPE]], ComputedVar[RETURN_TYPE]]: ... # pyright: ignore [reportInvalidTypeVarUse]
|
|
2263
2563
|
|
|
2264
2564
|
|
|
2265
2565
|
@overload
|
|
2266
2566
|
def computed_var(
|
|
2267
2567
|
fget: Callable[[BASE_STATE], RETURN_TYPE],
|
|
2268
2568
|
initial_value: RETURN_TYPE | types.Unset = types.Unset(),
|
|
2269
|
-
cache: bool =
|
|
2569
|
+
cache: bool = True,
|
|
2270
2570
|
deps: Optional[List[Union[str, Var]]] = None,
|
|
2271
2571
|
auto_deps: bool = True,
|
|
2272
2572
|
interval: Optional[Union[datetime.timedelta, int]] = None,
|
|
@@ -2278,7 +2578,7 @@ def computed_var(
|
|
|
2278
2578
|
def computed_var(
|
|
2279
2579
|
fget: Callable[[BASE_STATE], Any] | None = None,
|
|
2280
2580
|
initial_value: Any | types.Unset = types.Unset(),
|
|
2281
|
-
cache:
|
|
2581
|
+
cache: bool = True,
|
|
2282
2582
|
deps: Optional[List[Union[str, Var]]] = None,
|
|
2283
2583
|
auto_deps: bool = True,
|
|
2284
2584
|
interval: Optional[Union[datetime.timedelta, int]] = None,
|
|
@@ -2303,16 +2603,8 @@ def computed_var(
|
|
|
2303
2603
|
Raises:
|
|
2304
2604
|
ValueError: If caching is disabled and an update interval is set.
|
|
2305
2605
|
VarDependencyError: If user supplies dependencies without caching.
|
|
2606
|
+
ComputedVarSignatureError: If the getter function has more than one argument.
|
|
2306
2607
|
"""
|
|
2307
|
-
if cache is None:
|
|
2308
|
-
cache = False
|
|
2309
|
-
console.deprecate(
|
|
2310
|
-
"Default non-cached rx.var",
|
|
2311
|
-
"the default value will be `@rx.var(cache=True)` in a future release. "
|
|
2312
|
-
"To retain uncached var, explicitly pass `@rx.var(cache=False)`",
|
|
2313
|
-
deprecation_version="0.6.8",
|
|
2314
|
-
removal_version="0.7.0",
|
|
2315
|
-
)
|
|
2316
2608
|
if cache is False and interval is not None:
|
|
2317
2609
|
raise ValueError("Cannot set update interval without caching.")
|
|
2318
2610
|
|
|
@@ -2320,10 +2612,31 @@ def computed_var(
|
|
|
2320
2612
|
raise VarDependencyError("Cannot track dependencies without caching.")
|
|
2321
2613
|
|
|
2322
2614
|
if fget is not None:
|
|
2323
|
-
|
|
2615
|
+
sign = inspect.signature(fget)
|
|
2616
|
+
if len(sign.parameters) != 1:
|
|
2617
|
+
raise ComputedVarSignatureError(fget.__name__, signature=str(sign))
|
|
2618
|
+
|
|
2619
|
+
if inspect.iscoroutinefunction(fget):
|
|
2620
|
+
computed_var_cls = AsyncComputedVar
|
|
2621
|
+
else:
|
|
2622
|
+
computed_var_cls = ComputedVar
|
|
2623
|
+
return computed_var_cls(
|
|
2624
|
+
fget,
|
|
2625
|
+
initial_value=initial_value,
|
|
2626
|
+
cache=cache,
|
|
2627
|
+
deps=deps,
|
|
2628
|
+
auto_deps=auto_deps,
|
|
2629
|
+
interval=interval,
|
|
2630
|
+
backend=backend,
|
|
2631
|
+
**kwargs,
|
|
2632
|
+
)
|
|
2324
2633
|
|
|
2325
2634
|
def wrapper(fget: Callable[[BASE_STATE], Any]) -> ComputedVar:
|
|
2326
|
-
|
|
2635
|
+
if inspect.iscoroutinefunction(fget):
|
|
2636
|
+
computed_var_cls = AsyncComputedVar
|
|
2637
|
+
else:
|
|
2638
|
+
computed_var_cls = ComputedVar
|
|
2639
|
+
return computed_var_cls(
|
|
2327
2640
|
fget,
|
|
2328
2641
|
initial_value=initial_value,
|
|
2329
2642
|
cache=cache,
|
|
@@ -2392,7 +2705,7 @@ def var_operation_return(
|
|
|
2392
2705
|
@dataclasses.dataclass(
|
|
2393
2706
|
eq=False,
|
|
2394
2707
|
frozen=True,
|
|
2395
|
-
|
|
2708
|
+
slots=True,
|
|
2396
2709
|
)
|
|
2397
2710
|
class CustomVarOperation(CachedVarOperation, Var[T]):
|
|
2398
2711
|
"""Base class for custom var operations."""
|
|
@@ -2463,7 +2776,7 @@ class NoneVar(Var[None], python_types=type(None)):
|
|
|
2463
2776
|
@dataclasses.dataclass(
|
|
2464
2777
|
eq=False,
|
|
2465
2778
|
frozen=True,
|
|
2466
|
-
|
|
2779
|
+
slots=True,
|
|
2467
2780
|
)
|
|
2468
2781
|
class LiteralNoneVar(LiteralVar, NoneVar):
|
|
2469
2782
|
"""A var representing None."""
|
|
@@ -2525,7 +2838,7 @@ def get_to_operation(var_subclass: Type[Var]) -> Type[ToOperation]:
|
|
|
2525
2838
|
@dataclasses.dataclass(
|
|
2526
2839
|
eq=False,
|
|
2527
2840
|
frozen=True,
|
|
2528
|
-
|
|
2841
|
+
slots=True,
|
|
2529
2842
|
)
|
|
2530
2843
|
class StateOperation(CachedVarOperation, Var):
|
|
2531
2844
|
"""A var operation that accesses a field on an object."""
|
|
@@ -2597,7 +2910,7 @@ def get_uuid_string_var() -> Var:
|
|
|
2597
2910
|
unique_uuid_var = get_unique_variable_name()
|
|
2598
2911
|
unique_uuid_var_data = VarData(
|
|
2599
2912
|
imports={
|
|
2600
|
-
f"$/{constants.Dirs.STATE_PATH}": {ImportVar(tag="generateUUID")}, #
|
|
2913
|
+
f"$/{constants.Dirs.STATE_PATH}": {ImportVar(tag="generateUUID")}, # pyright: ignore [reportArgumentType]
|
|
2601
2914
|
"react": "useMemo",
|
|
2602
2915
|
},
|
|
2603
2916
|
hooks={f"const {unique_uuid_var} = useMemo(generateUUID, [])": None},
|
|
@@ -2657,7 +2970,7 @@ def _extract_var_data(value: Iterable) -> list[VarData | None]:
|
|
|
2657
2970
|
elif not isinstance(sub, str):
|
|
2658
2971
|
# Recurse into dict values.
|
|
2659
2972
|
if hasattr(sub, "values") and callable(sub.values):
|
|
2660
|
-
var_datas.extend(_extract_var_data(sub.values()))
|
|
2973
|
+
var_datas.extend(_extract_var_data(sub.values())) # pyright: ignore [reportArgumentType]
|
|
2661
2974
|
# Recurse into iterable values (or dict keys).
|
|
2662
2975
|
var_datas.extend(_extract_var_data(sub))
|
|
2663
2976
|
|
|
@@ -2668,23 +2981,10 @@ def _extract_var_data(value: Iterable) -> list[VarData | None]:
|
|
|
2668
2981
|
# Recurse when value is a dict itself.
|
|
2669
2982
|
values = getattr(value, "values", None)
|
|
2670
2983
|
if callable(values):
|
|
2671
|
-
var_datas.extend(_extract_var_data(values()))
|
|
2984
|
+
var_datas.extend(_extract_var_data(values())) # pyright: ignore [reportArgumentType]
|
|
2672
2985
|
return var_datas
|
|
2673
2986
|
|
|
2674
2987
|
|
|
2675
|
-
# These names were changed in reflex 0.3.0
|
|
2676
|
-
REPLACED_NAMES = {
|
|
2677
|
-
"full_name": "_var_full_name",
|
|
2678
|
-
"name": "_js_expr",
|
|
2679
|
-
"state": "_var_data.state",
|
|
2680
|
-
"type_": "_var_type",
|
|
2681
|
-
"is_local": "_var_is_local",
|
|
2682
|
-
"is_string": "_var_is_string",
|
|
2683
|
-
"set_state": "_var_set_state",
|
|
2684
|
-
"deps": "_deps",
|
|
2685
|
-
}
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
2988
|
dispatchers: Dict[GenericType, Callable[[Var], Var]] = {}
|
|
2689
2989
|
|
|
2690
2990
|
|
|
@@ -2765,7 +3065,7 @@ def generic_type_to_actual_type_map(
|
|
|
2765
3065
|
# call recursively for nested generic types and merge the results
|
|
2766
3066
|
return {
|
|
2767
3067
|
k: v
|
|
2768
|
-
for generic_arg, actual_arg in zip(generic_args, actual_args)
|
|
3068
|
+
for generic_arg, actual_arg in zip(generic_args, actual_args, strict=True)
|
|
2769
3069
|
for k, v in generic_type_to_actual_type_map(generic_arg, actual_arg).items()
|
|
2770
3070
|
}
|
|
2771
3071
|
|
|
@@ -2922,13 +3222,22 @@ def dispatch(
|
|
|
2922
3222
|
|
|
2923
3223
|
V = TypeVar("V")
|
|
2924
3224
|
|
|
2925
|
-
BASE_TYPE = TypeVar("BASE_TYPE", bound=Base)
|
|
3225
|
+
BASE_TYPE = TypeVar("BASE_TYPE", bound=Base | None)
|
|
3226
|
+
SQLA_TYPE = TypeVar("SQLA_TYPE", bound=DeclarativeBase | None)
|
|
2926
3227
|
|
|
3228
|
+
if TYPE_CHECKING:
|
|
3229
|
+
from _typeshed import DataclassInstance
|
|
2927
3230
|
|
|
2928
|
-
|
|
3231
|
+
DATACLASS_TYPE = TypeVar("DATACLASS_TYPE", bound=DataclassInstance | None)
|
|
3232
|
+
|
|
3233
|
+
FIELD_TYPE = TypeVar("FIELD_TYPE")
|
|
3234
|
+
MAPPING_TYPE = TypeVar("MAPPING_TYPE", bound=Mapping | None)
|
|
3235
|
+
|
|
3236
|
+
|
|
3237
|
+
class Field(Generic[FIELD_TYPE]):
|
|
2929
3238
|
"""Shadow class for Var to allow for type hinting in the IDE."""
|
|
2930
3239
|
|
|
2931
|
-
def __set__(self, instance, value:
|
|
3240
|
+
def __set__(self, instance: Any, value: FIELD_TYPE):
|
|
2932
3241
|
"""Set the Var.
|
|
2933
3242
|
|
|
2934
3243
|
Args:
|
|
@@ -2937,41 +3246,55 @@ class Field(Generic[T]):
|
|
|
2937
3246
|
"""
|
|
2938
3247
|
|
|
2939
3248
|
@overload
|
|
2940
|
-
def __get__(self: Field[bool], instance: None, owner) -> BooleanVar: ...
|
|
3249
|
+
def __get__(self: Field[bool], instance: None, owner: Any) -> BooleanVar: ...
|
|
2941
3250
|
|
|
2942
3251
|
@overload
|
|
2943
|
-
def __get__(
|
|
3252
|
+
def __get__(
|
|
3253
|
+
self: Field[int] | Field[float] | Field[int | float], instance: None, owner: Any
|
|
3254
|
+
) -> NumberVar: ...
|
|
2944
3255
|
|
|
2945
3256
|
@overload
|
|
2946
|
-
def __get__(self: Field[str], instance: None, owner) -> StringVar: ...
|
|
3257
|
+
def __get__(self: Field[str], instance: None, owner: Any) -> StringVar: ...
|
|
2947
3258
|
|
|
2948
3259
|
@overload
|
|
2949
|
-
def __get__(self: Field[None], instance: None, owner) -> NoneVar: ...
|
|
3260
|
+
def __get__(self: Field[None], instance: None, owner: Any) -> NoneVar: ...
|
|
2950
3261
|
|
|
2951
3262
|
@overload
|
|
2952
3263
|
def __get__(
|
|
2953
3264
|
self: Field[List[V]] | Field[Set[V]] | Field[Tuple[V, ...]],
|
|
2954
3265
|
instance: None,
|
|
2955
|
-
owner,
|
|
3266
|
+
owner: Any,
|
|
2956
3267
|
) -> ArrayVar[List[V]]: ...
|
|
2957
3268
|
|
|
2958
3269
|
@overload
|
|
2959
3270
|
def __get__(
|
|
2960
|
-
self: Field[
|
|
2961
|
-
) -> ObjectVar[
|
|
3271
|
+
self: Field[MAPPING_TYPE], instance: None, owner: Any
|
|
3272
|
+
) -> ObjectVar[MAPPING_TYPE]: ...
|
|
2962
3273
|
|
|
2963
3274
|
@overload
|
|
2964
3275
|
def __get__(
|
|
2965
|
-
self: Field[BASE_TYPE], instance: None, owner
|
|
3276
|
+
self: Field[BASE_TYPE], instance: None, owner: Any
|
|
2966
3277
|
) -> ObjectVar[BASE_TYPE]: ...
|
|
2967
3278
|
|
|
2968
3279
|
@overload
|
|
2969
|
-
def __get__(
|
|
3280
|
+
def __get__(
|
|
3281
|
+
self: Field[SQLA_TYPE], instance: None, owner: Any
|
|
3282
|
+
) -> ObjectVar[SQLA_TYPE]: ...
|
|
3283
|
+
|
|
3284
|
+
if TYPE_CHECKING:
|
|
3285
|
+
|
|
3286
|
+
@overload
|
|
3287
|
+
def __get__(
|
|
3288
|
+
self: Field[DATACLASS_TYPE], instance: None, owner: Any
|
|
3289
|
+
) -> ObjectVar[DATACLASS_TYPE]: ...
|
|
3290
|
+
|
|
3291
|
+
@overload
|
|
3292
|
+
def __get__(self, instance: None, owner: Any) -> Var[FIELD_TYPE]: ...
|
|
2970
3293
|
|
|
2971
3294
|
@overload
|
|
2972
|
-
def __get__(self, instance, owner) ->
|
|
3295
|
+
def __get__(self, instance: Any, owner: Any) -> FIELD_TYPE: ...
|
|
2973
3296
|
|
|
2974
|
-
def __get__(self, instance, owner): #
|
|
3297
|
+
def __get__(self, instance: Any, owner: Any): # pyright: ignore [reportInconsistentOverload]
|
|
2975
3298
|
"""Get the Var.
|
|
2976
3299
|
|
|
2977
3300
|
Args:
|
|
@@ -2980,7 +3303,7 @@ class Field(Generic[T]):
|
|
|
2980
3303
|
"""
|
|
2981
3304
|
|
|
2982
3305
|
|
|
2983
|
-
def field(value:
|
|
3306
|
+
def field(value: FIELD_TYPE) -> Field[FIELD_TYPE]:
|
|
2984
3307
|
"""Create a Field with a value.
|
|
2985
3308
|
|
|
2986
3309
|
Args:
|
|
@@ -2989,4 +3312,4 @@ def field(value: T) -> Field[T]:
|
|
|
2989
3312
|
Returns:
|
|
2990
3313
|
The Field.
|
|
2991
3314
|
"""
|
|
2992
|
-
return value #
|
|
3315
|
+
return value # pyright: ignore [reportReturnType]
|