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