reflex 0.6.8a2__py3-none-any.whl → 0.7.0__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 +286 -129
- 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 +57 -18
- 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 +134 -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/__init__.py +29 -2
- reflex/components/plotly/plotly.py +240 -5
- reflex/components/plotly/plotly.pyi +799 -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 +18 -10
- reflex/event.py +221 -231
- 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 +104 -26
- 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 +71 -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 +5 -2
- reflex/utils/lazy_loader.py +7 -1
- reflex/utils/path_ops.py +74 -14
- reflex/utils/prerequisites.py +345 -68
- 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 +695 -330
- 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 +74 -64
- reflex/vars/sequence.py +79 -67
- {reflex-0.6.8a2.dist-info → reflex-0.7.0.dist-info}/METADATA +7 -8
- reflex-0.7.0.dist-info/RECORD +401 -0
- {reflex-0.6.8a2.dist-info → reflex-0.7.0.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.0.dist-info}/LICENSE +0 -0
- {reflex-0.6.8a2.dist-info → reflex-0.7.0.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_TYPE],
|
|
2006
2223
|
instance: None,
|
|
2007
2224
|
owner: Type,
|
|
2008
|
-
) -> ObjectVar[
|
|
2225
|
+
) -> ObjectVar[MAPPING_TYPE]: ...
|
|
2009
2226
|
|
|
2010
2227
|
@overload
|
|
2011
2228
|
def __get__(
|
|
@@ -2016,17 +2233,31 @@ class ComputedVar(Var[RETURN_TYPE]):
|
|
|
2016
2233
|
|
|
2017
2234
|
@overload
|
|
2018
2235
|
def __get__(
|
|
2019
|
-
self: ComputedVar[
|
|
2236
|
+
self: ComputedVar[tuple[LIST_INSIDE, ...]],
|
|
2020
2237
|
instance: None,
|
|
2021
2238
|
owner: Type,
|
|
2022
|
-
) -> ArrayVar[
|
|
2239
|
+
) -> ArrayVar[tuple[LIST_INSIDE, ...]]: ...
|
|
2023
2240
|
|
|
2024
2241
|
@overload
|
|
2025
2242
|
def __get__(
|
|
2026
|
-
self: ComputedVar[
|
|
2243
|
+
self: ComputedVar[BASE_TYPE],
|
|
2027
2244
|
instance: None,
|
|
2028
2245
|
owner: Type,
|
|
2029
|
-
) ->
|
|
2246
|
+
) -> ObjectVar[BASE_TYPE]: ...
|
|
2247
|
+
|
|
2248
|
+
@overload
|
|
2249
|
+
def __get__(
|
|
2250
|
+
self: ComputedVar[SQLA_TYPE],
|
|
2251
|
+
instance: None,
|
|
2252
|
+
owner: Type,
|
|
2253
|
+
) -> ObjectVar[SQLA_TYPE]: ...
|
|
2254
|
+
|
|
2255
|
+
if TYPE_CHECKING:
|
|
2256
|
+
|
|
2257
|
+
@overload
|
|
2258
|
+
def __get__(
|
|
2259
|
+
self: ComputedVar[DATACLASS_TYPE], instance: None, owner: Any
|
|
2260
|
+
) -> ObjectVar[DATACLASS_TYPE]: ...
|
|
2030
2261
|
|
|
2031
2262
|
@overload
|
|
2032
2263
|
def __get__(self, instance: None, owner: Type) -> ComputedVar[RETURN_TYPE]: ...
|
|
@@ -2034,7 +2265,7 @@ class ComputedVar(Var[RETURN_TYPE]):
|
|
|
2034
2265
|
@overload
|
|
2035
2266
|
def __get__(self, instance: BaseState, owner: Type) -> RETURN_TYPE: ...
|
|
2036
2267
|
|
|
2037
|
-
def __get__(self, instance: BaseState | None, owner):
|
|
2268
|
+
def __get__(self, instance: BaseState | None, owner: Type):
|
|
2038
2269
|
"""Get the ComputedVar value.
|
|
2039
2270
|
|
|
2040
2271
|
If the value is already cached on the instance, return the cached value.
|
|
@@ -2077,130 +2308,69 @@ class ComputedVar(Var[RETURN_TYPE]):
|
|
|
2077
2308
|
setattr(instance, self._last_updated_attr, datetime.datetime.now())
|
|
2078
2309
|
value = getattr(instance, self._cache_attr)
|
|
2079
2310
|
|
|
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
|
-
)
|
|
2311
|
+
self._check_deprecated_return_type(instance, value)
|
|
2088
2312
|
|
|
2089
2313
|
return value
|
|
2090
2314
|
|
|
2315
|
+
def _check_deprecated_return_type(self, instance: BaseState, value: Any) -> None:
|
|
2316
|
+
if not _isinstance(value, self._var_type):
|
|
2317
|
+
console.error(
|
|
2318
|
+
f"Computed var '{type(instance).__name__}.{self._js_expr}' must return"
|
|
2319
|
+
f" type '{self._var_type}', got '{type(value)}'."
|
|
2320
|
+
)
|
|
2321
|
+
|
|
2091
2322
|
def _deps(
|
|
2092
2323
|
self,
|
|
2093
|
-
objclass: Type,
|
|
2324
|
+
objclass: Type[BaseState],
|
|
2094
2325
|
obj: FunctionType | CodeType | None = None,
|
|
2095
|
-
|
|
2096
|
-
) -> set[str]:
|
|
2326
|
+
) -> dict[str, set[str]]:
|
|
2097
2327
|
"""Determine var dependencies of this ComputedVar.
|
|
2098
2328
|
|
|
2099
|
-
Save references to attributes accessed on "self".
|
|
2100
|
-
|
|
2101
|
-
|
|
2329
|
+
Save references to attributes accessed on "self" or other fetched states.
|
|
2330
|
+
|
|
2331
|
+
Recursively called when the function makes a method call on "self" or
|
|
2332
|
+
define comprehensions or nested functions that may reference "self".
|
|
2102
2333
|
|
|
2103
2334
|
Args:
|
|
2104
2335
|
objclass: the class obj this ComputedVar is attached to.
|
|
2105
2336
|
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
2337
|
|
|
2108
2338
|
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).
|
|
2339
|
+
A dictionary mapping state names to the set of variable names
|
|
2340
|
+
accessed by the given obj.
|
|
2114
2341
|
"""
|
|
2342
|
+
from .dep_tracking import DependencyTracker
|
|
2343
|
+
|
|
2344
|
+
d = {}
|
|
2345
|
+
if self._static_deps:
|
|
2346
|
+
d.update(self._static_deps)
|
|
2347
|
+
# None is a placeholder for the current state class.
|
|
2348
|
+
if None in d:
|
|
2349
|
+
d[objclass.get_full_name()] = d.pop(None)
|
|
2350
|
+
|
|
2115
2351
|
if not self._auto_deps:
|
|
2116
|
-
return
|
|
2117
|
-
|
|
2352
|
+
return d
|
|
2353
|
+
|
|
2118
2354
|
if obj is None:
|
|
2119
2355
|
fget = self._fget
|
|
2120
2356
|
if fget is not None:
|
|
2121
2357
|
obj = cast(FunctionType, fget)
|
|
2122
2358
|
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
|
|
2359
|
+
return d
|
|
2130
2360
|
|
|
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
|
|
2361
|
+
try:
|
|
2362
|
+
return DependencyTracker(
|
|
2363
|
+
func=obj, state_cls=objclass, dependencies=d
|
|
2364
|
+
).dependencies
|
|
2365
|
+
except Exception as e:
|
|
2366
|
+
console.warn(
|
|
2367
|
+
"Failed to automatically determine dependencies for computed var "
|
|
2368
|
+
f"{objclass.__name__}.{self._js_expr}: {e}. "
|
|
2369
|
+
"Provide static_deps and set auto_deps=False to suppress this warning."
|
|
2370
|
+
)
|
|
2371
|
+
return d
|
|
2202
2372
|
|
|
2203
|
-
def mark_dirty(self, instance) -> None:
|
|
2373
|
+
def mark_dirty(self, instance: BaseState) -> None:
|
|
2204
2374
|
"""Mark this ComputedVar as dirty.
|
|
2205
2375
|
|
|
2206
2376
|
Args:
|
|
@@ -2209,6 +2379,37 @@ class ComputedVar(Var[RETURN_TYPE]):
|
|
|
2209
2379
|
with contextlib.suppress(AttributeError):
|
|
2210
2380
|
delattr(instance, self._cache_attr)
|
|
2211
2381
|
|
|
2382
|
+
def add_dependency(self, objclass: Type[BaseState], dep: Var):
|
|
2383
|
+
"""Explicitly add a dependency to the ComputedVar.
|
|
2384
|
+
|
|
2385
|
+
After adding the dependency, when the `dep` changes, this computed var
|
|
2386
|
+
will be marked dirty.
|
|
2387
|
+
|
|
2388
|
+
Args:
|
|
2389
|
+
objclass: The class obj this ComputedVar is attached to.
|
|
2390
|
+
dep: The dependency to add.
|
|
2391
|
+
|
|
2392
|
+
Raises:
|
|
2393
|
+
VarDependencyError: If the dependency is not a Var instance with a
|
|
2394
|
+
state and field name
|
|
2395
|
+
"""
|
|
2396
|
+
if all_var_data := dep._get_all_var_data():
|
|
2397
|
+
state_name = all_var_data.state
|
|
2398
|
+
if state_name:
|
|
2399
|
+
var_name = all_var_data.field_name
|
|
2400
|
+
if var_name:
|
|
2401
|
+
self._static_deps.setdefault(state_name, set()).add(var_name)
|
|
2402
|
+
objclass.get_root_state().get_class_substate(
|
|
2403
|
+
state_name
|
|
2404
|
+
)._var_dependencies.setdefault(var_name, set()).add(
|
|
2405
|
+
(objclass.get_full_name(), self._js_expr)
|
|
2406
|
+
)
|
|
2407
|
+
return
|
|
2408
|
+
raise VarDependencyError(
|
|
2409
|
+
"ComputedVar dependencies must be Var instances with a state and "
|
|
2410
|
+
f"field name, got {dep!r}."
|
|
2411
|
+
)
|
|
2412
|
+
|
|
2212
2413
|
def _determine_var_type(self) -> Type:
|
|
2213
2414
|
"""Get the type of the var.
|
|
2214
2415
|
|
|
@@ -2218,7 +2419,7 @@ class ComputedVar(Var[RETURN_TYPE]):
|
|
|
2218
2419
|
hints = get_type_hints(self._fget)
|
|
2219
2420
|
if "return" in hints:
|
|
2220
2421
|
return hints["return"]
|
|
2221
|
-
return Any
|
|
2422
|
+
return Any # pyright: ignore [reportReturnType]
|
|
2222
2423
|
|
|
2223
2424
|
@property
|
|
2224
2425
|
def __class__(self) -> Type:
|
|
@@ -2245,6 +2446,147 @@ class DynamicRouteVar(ComputedVar[Union[str, List[str]]]):
|
|
|
2245
2446
|
pass
|
|
2246
2447
|
|
|
2247
2448
|
|
|
2449
|
+
async def _default_async_computed_var(_self: BaseState) -> Any:
|
|
2450
|
+
return None
|
|
2451
|
+
|
|
2452
|
+
|
|
2453
|
+
@dataclasses.dataclass(
|
|
2454
|
+
eq=False,
|
|
2455
|
+
frozen=True,
|
|
2456
|
+
init=False,
|
|
2457
|
+
slots=True,
|
|
2458
|
+
)
|
|
2459
|
+
class AsyncComputedVar(ComputedVar[RETURN_TYPE]):
|
|
2460
|
+
"""A computed var that wraps a coroutinefunction."""
|
|
2461
|
+
|
|
2462
|
+
_fget: Callable[[BaseState], Coroutine[None, None, RETURN_TYPE]] = (
|
|
2463
|
+
dataclasses.field(default=_default_async_computed_var)
|
|
2464
|
+
)
|
|
2465
|
+
|
|
2466
|
+
@overload
|
|
2467
|
+
def __get__(
|
|
2468
|
+
self: AsyncComputedVar[bool],
|
|
2469
|
+
instance: None,
|
|
2470
|
+
owner: Type,
|
|
2471
|
+
) -> BooleanVar: ...
|
|
2472
|
+
|
|
2473
|
+
@overload
|
|
2474
|
+
def __get__(
|
|
2475
|
+
self: AsyncComputedVar[int] | ComputedVar[float],
|
|
2476
|
+
instance: None,
|
|
2477
|
+
owner: Type,
|
|
2478
|
+
) -> NumberVar: ...
|
|
2479
|
+
|
|
2480
|
+
@overload
|
|
2481
|
+
def __get__(
|
|
2482
|
+
self: AsyncComputedVar[str],
|
|
2483
|
+
instance: None,
|
|
2484
|
+
owner: Type,
|
|
2485
|
+
) -> StringVar: ...
|
|
2486
|
+
|
|
2487
|
+
@overload
|
|
2488
|
+
def __get__(
|
|
2489
|
+
self: AsyncComputedVar[MAPPING_TYPE],
|
|
2490
|
+
instance: None,
|
|
2491
|
+
owner: Type,
|
|
2492
|
+
) -> ObjectVar[MAPPING_TYPE]: ...
|
|
2493
|
+
|
|
2494
|
+
@overload
|
|
2495
|
+
def __get__(
|
|
2496
|
+
self: AsyncComputedVar[list[LIST_INSIDE]],
|
|
2497
|
+
instance: None,
|
|
2498
|
+
owner: Type,
|
|
2499
|
+
) -> ArrayVar[list[LIST_INSIDE]]: ...
|
|
2500
|
+
|
|
2501
|
+
@overload
|
|
2502
|
+
def __get__(
|
|
2503
|
+
self: AsyncComputedVar[tuple[LIST_INSIDE, ...]],
|
|
2504
|
+
instance: None,
|
|
2505
|
+
owner: Type,
|
|
2506
|
+
) -> ArrayVar[tuple[LIST_INSIDE, ...]]: ...
|
|
2507
|
+
|
|
2508
|
+
@overload
|
|
2509
|
+
def __get__(
|
|
2510
|
+
self: AsyncComputedVar[BASE_TYPE],
|
|
2511
|
+
instance: None,
|
|
2512
|
+
owner: Type,
|
|
2513
|
+
) -> ObjectVar[BASE_TYPE]: ...
|
|
2514
|
+
|
|
2515
|
+
@overload
|
|
2516
|
+
def __get__(
|
|
2517
|
+
self: AsyncComputedVar[SQLA_TYPE],
|
|
2518
|
+
instance: None,
|
|
2519
|
+
owner: Type,
|
|
2520
|
+
) -> ObjectVar[SQLA_TYPE]: ...
|
|
2521
|
+
|
|
2522
|
+
if TYPE_CHECKING:
|
|
2523
|
+
|
|
2524
|
+
@overload
|
|
2525
|
+
def __get__(
|
|
2526
|
+
self: AsyncComputedVar[DATACLASS_TYPE], instance: None, owner: Any
|
|
2527
|
+
) -> ObjectVar[DATACLASS_TYPE]: ...
|
|
2528
|
+
|
|
2529
|
+
@overload
|
|
2530
|
+
def __get__(self, instance: None, owner: Type) -> AsyncComputedVar[RETURN_TYPE]: ...
|
|
2531
|
+
|
|
2532
|
+
@overload
|
|
2533
|
+
def __get__(
|
|
2534
|
+
self, instance: BaseState, owner: Type
|
|
2535
|
+
) -> Coroutine[None, None, RETURN_TYPE]: ...
|
|
2536
|
+
|
|
2537
|
+
def __get__(
|
|
2538
|
+
self, instance: BaseState | None, owner
|
|
2539
|
+
) -> Var | Coroutine[None, None, RETURN_TYPE]:
|
|
2540
|
+
"""Get the ComputedVar value.
|
|
2541
|
+
|
|
2542
|
+
If the value is already cached on the instance, return the cached value.
|
|
2543
|
+
|
|
2544
|
+
Args:
|
|
2545
|
+
instance: the instance of the class accessing this computed var.
|
|
2546
|
+
owner: the class that this descriptor is attached to.
|
|
2547
|
+
|
|
2548
|
+
Returns:
|
|
2549
|
+
The value of the var for the given instance.
|
|
2550
|
+
"""
|
|
2551
|
+
if instance is None:
|
|
2552
|
+
return super(AsyncComputedVar, self).__get__(instance, owner)
|
|
2553
|
+
|
|
2554
|
+
if not self._cache:
|
|
2555
|
+
|
|
2556
|
+
async def _awaitable_result(instance: BaseState = instance) -> RETURN_TYPE:
|
|
2557
|
+
value = await self.fget(instance)
|
|
2558
|
+
self._check_deprecated_return_type(instance, value)
|
|
2559
|
+
return value
|
|
2560
|
+
|
|
2561
|
+
return _awaitable_result()
|
|
2562
|
+
else:
|
|
2563
|
+
# handle caching
|
|
2564
|
+
async def _awaitable_result(instance: BaseState = instance) -> RETURN_TYPE:
|
|
2565
|
+
if not hasattr(instance, self._cache_attr) or self.needs_update(
|
|
2566
|
+
instance
|
|
2567
|
+
):
|
|
2568
|
+
# Set cache attr on state instance.
|
|
2569
|
+
setattr(instance, self._cache_attr, await self.fget(instance))
|
|
2570
|
+
# Ensure the computed var gets serialized to redis.
|
|
2571
|
+
instance._was_touched = True
|
|
2572
|
+
# Set the last updated timestamp on the state instance.
|
|
2573
|
+
setattr(instance, self._last_updated_attr, datetime.datetime.now())
|
|
2574
|
+
value = getattr(instance, self._cache_attr)
|
|
2575
|
+
self._check_deprecated_return_type(instance, value)
|
|
2576
|
+
return value
|
|
2577
|
+
|
|
2578
|
+
return _awaitable_result()
|
|
2579
|
+
|
|
2580
|
+
@property
|
|
2581
|
+
def fget(self) -> Callable[[BaseState], Coroutine[None, None, RETURN_TYPE]]:
|
|
2582
|
+
"""Get the getter function.
|
|
2583
|
+
|
|
2584
|
+
Returns:
|
|
2585
|
+
The getter function.
|
|
2586
|
+
"""
|
|
2587
|
+
return self._fget
|
|
2588
|
+
|
|
2589
|
+
|
|
2248
2590
|
if TYPE_CHECKING:
|
|
2249
2591
|
BASE_STATE = TypeVar("BASE_STATE", bound=BaseState)
|
|
2250
2592
|
|
|
@@ -2253,20 +2595,20 @@ if TYPE_CHECKING:
|
|
|
2253
2595
|
def computed_var(
|
|
2254
2596
|
fget: None = None,
|
|
2255
2597
|
initial_value: Any | types.Unset = types.Unset(),
|
|
2256
|
-
cache: bool =
|
|
2598
|
+
cache: bool = True,
|
|
2257
2599
|
deps: Optional[List[Union[str, Var]]] = None,
|
|
2258
2600
|
auto_deps: bool = True,
|
|
2259
2601
|
interval: Optional[Union[datetime.timedelta, int]] = None,
|
|
2260
2602
|
backend: bool | None = None,
|
|
2261
2603
|
**kwargs,
|
|
2262
|
-
) -> Callable[[Callable[[BASE_STATE], RETURN_TYPE]], ComputedVar[RETURN_TYPE]]: ...
|
|
2604
|
+
) -> Callable[[Callable[[BASE_STATE], RETURN_TYPE]], ComputedVar[RETURN_TYPE]]: ... # pyright: ignore [reportInvalidTypeVarUse]
|
|
2263
2605
|
|
|
2264
2606
|
|
|
2265
2607
|
@overload
|
|
2266
2608
|
def computed_var(
|
|
2267
2609
|
fget: Callable[[BASE_STATE], RETURN_TYPE],
|
|
2268
2610
|
initial_value: RETURN_TYPE | types.Unset = types.Unset(),
|
|
2269
|
-
cache: bool =
|
|
2611
|
+
cache: bool = True,
|
|
2270
2612
|
deps: Optional[List[Union[str, Var]]] = None,
|
|
2271
2613
|
auto_deps: bool = True,
|
|
2272
2614
|
interval: Optional[Union[datetime.timedelta, int]] = None,
|
|
@@ -2278,7 +2620,7 @@ def computed_var(
|
|
|
2278
2620
|
def computed_var(
|
|
2279
2621
|
fget: Callable[[BASE_STATE], Any] | None = None,
|
|
2280
2622
|
initial_value: Any | types.Unset = types.Unset(),
|
|
2281
|
-
cache:
|
|
2623
|
+
cache: bool = True,
|
|
2282
2624
|
deps: Optional[List[Union[str, Var]]] = None,
|
|
2283
2625
|
auto_deps: bool = True,
|
|
2284
2626
|
interval: Optional[Union[datetime.timedelta, int]] = None,
|
|
@@ -2303,16 +2645,8 @@ def computed_var(
|
|
|
2303
2645
|
Raises:
|
|
2304
2646
|
ValueError: If caching is disabled and an update interval is set.
|
|
2305
2647
|
VarDependencyError: If user supplies dependencies without caching.
|
|
2648
|
+
ComputedVarSignatureError: If the getter function has more than one argument.
|
|
2306
2649
|
"""
|
|
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
2650
|
if cache is False and interval is not None:
|
|
2317
2651
|
raise ValueError("Cannot set update interval without caching.")
|
|
2318
2652
|
|
|
@@ -2320,10 +2654,31 @@ def computed_var(
|
|
|
2320
2654
|
raise VarDependencyError("Cannot track dependencies without caching.")
|
|
2321
2655
|
|
|
2322
2656
|
if fget is not None:
|
|
2323
|
-
|
|
2657
|
+
sign = inspect.signature(fget)
|
|
2658
|
+
if len(sign.parameters) != 1:
|
|
2659
|
+
raise ComputedVarSignatureError(fget.__name__, signature=str(sign))
|
|
2660
|
+
|
|
2661
|
+
if inspect.iscoroutinefunction(fget):
|
|
2662
|
+
computed_var_cls = AsyncComputedVar
|
|
2663
|
+
else:
|
|
2664
|
+
computed_var_cls = ComputedVar
|
|
2665
|
+
return computed_var_cls(
|
|
2666
|
+
fget,
|
|
2667
|
+
initial_value=initial_value,
|
|
2668
|
+
cache=cache,
|
|
2669
|
+
deps=deps,
|
|
2670
|
+
auto_deps=auto_deps,
|
|
2671
|
+
interval=interval,
|
|
2672
|
+
backend=backend,
|
|
2673
|
+
**kwargs,
|
|
2674
|
+
)
|
|
2324
2675
|
|
|
2325
2676
|
def wrapper(fget: Callable[[BASE_STATE], Any]) -> ComputedVar:
|
|
2326
|
-
|
|
2677
|
+
if inspect.iscoroutinefunction(fget):
|
|
2678
|
+
computed_var_cls = AsyncComputedVar
|
|
2679
|
+
else:
|
|
2680
|
+
computed_var_cls = ComputedVar
|
|
2681
|
+
return computed_var_cls(
|
|
2327
2682
|
fget,
|
|
2328
2683
|
initial_value=initial_value,
|
|
2329
2684
|
cache=cache,
|
|
@@ -2392,7 +2747,7 @@ def var_operation_return(
|
|
|
2392
2747
|
@dataclasses.dataclass(
|
|
2393
2748
|
eq=False,
|
|
2394
2749
|
frozen=True,
|
|
2395
|
-
|
|
2750
|
+
slots=True,
|
|
2396
2751
|
)
|
|
2397
2752
|
class CustomVarOperation(CachedVarOperation, Var[T]):
|
|
2398
2753
|
"""Base class for custom var operations."""
|
|
@@ -2463,7 +2818,7 @@ class NoneVar(Var[None], python_types=type(None)):
|
|
|
2463
2818
|
@dataclasses.dataclass(
|
|
2464
2819
|
eq=False,
|
|
2465
2820
|
frozen=True,
|
|
2466
|
-
|
|
2821
|
+
slots=True,
|
|
2467
2822
|
)
|
|
2468
2823
|
class LiteralNoneVar(LiteralVar, NoneVar):
|
|
2469
2824
|
"""A var representing None."""
|
|
@@ -2525,7 +2880,7 @@ def get_to_operation(var_subclass: Type[Var]) -> Type[ToOperation]:
|
|
|
2525
2880
|
@dataclasses.dataclass(
|
|
2526
2881
|
eq=False,
|
|
2527
2882
|
frozen=True,
|
|
2528
|
-
|
|
2883
|
+
slots=True,
|
|
2529
2884
|
)
|
|
2530
2885
|
class StateOperation(CachedVarOperation, Var):
|
|
2531
2886
|
"""A var operation that accesses a field on an object."""
|
|
@@ -2597,7 +2952,7 @@ def get_uuid_string_var() -> Var:
|
|
|
2597
2952
|
unique_uuid_var = get_unique_variable_name()
|
|
2598
2953
|
unique_uuid_var_data = VarData(
|
|
2599
2954
|
imports={
|
|
2600
|
-
f"$/{constants.Dirs.STATE_PATH}": {ImportVar(tag="generateUUID")}, #
|
|
2955
|
+
f"$/{constants.Dirs.STATE_PATH}": {ImportVar(tag="generateUUID")}, # pyright: ignore [reportArgumentType]
|
|
2601
2956
|
"react": "useMemo",
|
|
2602
2957
|
},
|
|
2603
2958
|
hooks={f"const {unique_uuid_var} = useMemo(generateUUID, [])": None},
|
|
@@ -2657,7 +3012,7 @@ def _extract_var_data(value: Iterable) -> list[VarData | None]:
|
|
|
2657
3012
|
elif not isinstance(sub, str):
|
|
2658
3013
|
# Recurse into dict values.
|
|
2659
3014
|
if hasattr(sub, "values") and callable(sub.values):
|
|
2660
|
-
var_datas.extend(_extract_var_data(sub.values()))
|
|
3015
|
+
var_datas.extend(_extract_var_data(sub.values())) # pyright: ignore [reportArgumentType]
|
|
2661
3016
|
# Recurse into iterable values (or dict keys).
|
|
2662
3017
|
var_datas.extend(_extract_var_data(sub))
|
|
2663
3018
|
|
|
@@ -2668,23 +3023,10 @@ def _extract_var_data(value: Iterable) -> list[VarData | None]:
|
|
|
2668
3023
|
# Recurse when value is a dict itself.
|
|
2669
3024
|
values = getattr(value, "values", None)
|
|
2670
3025
|
if callable(values):
|
|
2671
|
-
var_datas.extend(_extract_var_data(values()))
|
|
3026
|
+
var_datas.extend(_extract_var_data(values())) # pyright: ignore [reportArgumentType]
|
|
2672
3027
|
return var_datas
|
|
2673
3028
|
|
|
2674
3029
|
|
|
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
3030
|
dispatchers: Dict[GenericType, Callable[[Var], Var]] = {}
|
|
2689
3031
|
|
|
2690
3032
|
|
|
@@ -2765,7 +3107,7 @@ def generic_type_to_actual_type_map(
|
|
|
2765
3107
|
# call recursively for nested generic types and merge the results
|
|
2766
3108
|
return {
|
|
2767
3109
|
k: v
|
|
2768
|
-
for generic_arg, actual_arg in zip(generic_args, actual_args)
|
|
3110
|
+
for generic_arg, actual_arg in zip(generic_args, actual_args, strict=True)
|
|
2769
3111
|
for k, v in generic_type_to_actual_type_map(generic_arg, actual_arg).items()
|
|
2770
3112
|
}
|
|
2771
3113
|
|
|
@@ -2922,13 +3264,22 @@ def dispatch(
|
|
|
2922
3264
|
|
|
2923
3265
|
V = TypeVar("V")
|
|
2924
3266
|
|
|
2925
|
-
BASE_TYPE = TypeVar("BASE_TYPE", bound=Base)
|
|
3267
|
+
BASE_TYPE = TypeVar("BASE_TYPE", bound=Base | None)
|
|
3268
|
+
SQLA_TYPE = TypeVar("SQLA_TYPE", bound=DeclarativeBase | None)
|
|
2926
3269
|
|
|
3270
|
+
if TYPE_CHECKING:
|
|
3271
|
+
from _typeshed import DataclassInstance
|
|
3272
|
+
|
|
3273
|
+
DATACLASS_TYPE = TypeVar("DATACLASS_TYPE", bound=DataclassInstance | None)
|
|
3274
|
+
|
|
3275
|
+
FIELD_TYPE = TypeVar("FIELD_TYPE")
|
|
3276
|
+
MAPPING_TYPE = TypeVar("MAPPING_TYPE", bound=Mapping | None)
|
|
2927
3277
|
|
|
2928
|
-
|
|
3278
|
+
|
|
3279
|
+
class Field(Generic[FIELD_TYPE]):
|
|
2929
3280
|
"""Shadow class for Var to allow for type hinting in the IDE."""
|
|
2930
3281
|
|
|
2931
|
-
def __set__(self, instance, value:
|
|
3282
|
+
def __set__(self, instance: Any, value: FIELD_TYPE):
|
|
2932
3283
|
"""Set the Var.
|
|
2933
3284
|
|
|
2934
3285
|
Args:
|
|
@@ -2937,41 +3288,55 @@ class Field(Generic[T]):
|
|
|
2937
3288
|
"""
|
|
2938
3289
|
|
|
2939
3290
|
@overload
|
|
2940
|
-
def __get__(self: Field[bool], instance: None, owner) -> BooleanVar: ...
|
|
3291
|
+
def __get__(self: Field[bool], instance: None, owner: Any) -> BooleanVar: ...
|
|
2941
3292
|
|
|
2942
3293
|
@overload
|
|
2943
|
-
def __get__(
|
|
3294
|
+
def __get__(
|
|
3295
|
+
self: Field[int] | Field[float] | Field[int | float], instance: None, owner: Any
|
|
3296
|
+
) -> NumberVar: ...
|
|
2944
3297
|
|
|
2945
3298
|
@overload
|
|
2946
|
-
def __get__(self: Field[str], instance: None, owner) -> StringVar: ...
|
|
3299
|
+
def __get__(self: Field[str], instance: None, owner: Any) -> StringVar: ...
|
|
2947
3300
|
|
|
2948
3301
|
@overload
|
|
2949
|
-
def __get__(self: Field[None], instance: None, owner) -> NoneVar: ...
|
|
3302
|
+
def __get__(self: Field[None], instance: None, owner: Any) -> NoneVar: ...
|
|
2950
3303
|
|
|
2951
3304
|
@overload
|
|
2952
3305
|
def __get__(
|
|
2953
3306
|
self: Field[List[V]] | Field[Set[V]] | Field[Tuple[V, ...]],
|
|
2954
3307
|
instance: None,
|
|
2955
|
-
owner,
|
|
3308
|
+
owner: Any,
|
|
2956
3309
|
) -> ArrayVar[List[V]]: ...
|
|
2957
3310
|
|
|
2958
3311
|
@overload
|
|
2959
3312
|
def __get__(
|
|
2960
|
-
self: Field[
|
|
2961
|
-
) -> ObjectVar[
|
|
3313
|
+
self: Field[MAPPING_TYPE], instance: None, owner: Any
|
|
3314
|
+
) -> ObjectVar[MAPPING_TYPE]: ...
|
|
2962
3315
|
|
|
2963
3316
|
@overload
|
|
2964
3317
|
def __get__(
|
|
2965
|
-
self: Field[BASE_TYPE], instance: None, owner
|
|
3318
|
+
self: Field[BASE_TYPE], instance: None, owner: Any
|
|
2966
3319
|
) -> ObjectVar[BASE_TYPE]: ...
|
|
2967
3320
|
|
|
2968
3321
|
@overload
|
|
2969
|
-
def __get__(
|
|
3322
|
+
def __get__(
|
|
3323
|
+
self: Field[SQLA_TYPE], instance: None, owner: Any
|
|
3324
|
+
) -> ObjectVar[SQLA_TYPE]: ...
|
|
3325
|
+
|
|
3326
|
+
if TYPE_CHECKING:
|
|
3327
|
+
|
|
3328
|
+
@overload
|
|
3329
|
+
def __get__(
|
|
3330
|
+
self: Field[DATACLASS_TYPE], instance: None, owner: Any
|
|
3331
|
+
) -> ObjectVar[DATACLASS_TYPE]: ...
|
|
3332
|
+
|
|
3333
|
+
@overload
|
|
3334
|
+
def __get__(self, instance: None, owner: Any) -> Var[FIELD_TYPE]: ...
|
|
2970
3335
|
|
|
2971
3336
|
@overload
|
|
2972
|
-
def __get__(self, instance, owner) ->
|
|
3337
|
+
def __get__(self, instance: Any, owner: Any) -> FIELD_TYPE: ...
|
|
2973
3338
|
|
|
2974
|
-
def __get__(self, instance, owner): #
|
|
3339
|
+
def __get__(self, instance: Any, owner: Any): # pyright: ignore [reportInconsistentOverload]
|
|
2975
3340
|
"""Get the Var.
|
|
2976
3341
|
|
|
2977
3342
|
Args:
|
|
@@ -2980,7 +3345,7 @@ class Field(Generic[T]):
|
|
|
2980
3345
|
"""
|
|
2981
3346
|
|
|
2982
3347
|
|
|
2983
|
-
def field(value:
|
|
3348
|
+
def field(value: FIELD_TYPE) -> Field[FIELD_TYPE]:
|
|
2984
3349
|
"""Create a Field with a value.
|
|
2985
3350
|
|
|
2986
3351
|
Args:
|
|
@@ -2989,4 +3354,4 @@ def field(value: T) -> Field[T]:
|
|
|
2989
3354
|
Returns:
|
|
2990
3355
|
The Field.
|
|
2991
3356
|
"""
|
|
2992
|
-
return value #
|
|
3357
|
+
return value # pyright: ignore [reportReturnType]
|