reflex 0.7.14a6__py3-none-any.whl → 0.8.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/app/rxconfig.py.jinja2 +4 -1
- reflex/.templates/jinja/web/package.json.jinja2 +1 -1
- reflex/.templates/jinja/web/pages/_app.js.jinja2 +21 -11
- reflex/.templates/jinja/web/pages/_document.js.jinja2 +1 -1
- reflex/.templates/jinja/web/pages/base_page.js.jinja2 +0 -1
- reflex/.templates/jinja/web/pages/stateful_component.js.jinja2 +4 -0
- reflex/.templates/jinja/web/styles/styles.css.jinja2 +1 -0
- reflex/.templates/jinja/web/utils/context.js.jinja2 +25 -8
- reflex/.templates/web/app/entry.client.js +8 -0
- reflex/.templates/web/app/routes.js +10 -0
- reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js +12 -37
- reflex/.templates/web/postcss.config.js +1 -1
- reflex/.templates/web/react-router.config.js +6 -0
- reflex/.templates/web/styles/__reflex_style_reset.css +399 -0
- reflex/.templates/web/utils/client_side_routing.js +21 -19
- reflex/.templates/web/utils/react-theme.js +92 -0
- reflex/.templates/web/utils/state.js +251 -100
- reflex/.templates/web/vite-plugin-safari-cachebust.js +160 -0
- reflex/.templates/web/vite.config.js +39 -0
- reflex/__init__.py +1 -6
- reflex/__init__.pyi +327 -192
- reflex/app.py +86 -135
- reflex/base.py +1 -87
- reflex/compiler/compiler.py +70 -19
- reflex/compiler/templates.py +3 -3
- reflex/compiler/utils.py +91 -33
- reflex/components/__init__.py +0 -2
- reflex/components/__init__.pyi +34 -18
- reflex/components/base/__init__.py +1 -5
- reflex/components/base/__init__.pyi +30 -21
- reflex/components/base/app_wrap.pyi +7 -7
- reflex/components/base/body.pyi +7 -7
- reflex/components/base/document.py +18 -14
- reflex/components/base/document.pyi +88 -38
- reflex/components/base/error_boundary.pyi +7 -7
- reflex/components/base/fragment.pyi +7 -7
- reflex/components/base/link.pyi +12 -12
- reflex/components/base/meta.py +4 -15
- reflex/components/base/meta.pyi +31 -31
- reflex/components/base/script.py +60 -58
- reflex/components/base/script.pyi +248 -34
- reflex/components/base/strict_mode.pyi +7 -7
- reflex/components/component.py +146 -217
- reflex/components/core/__init__.py +1 -0
- reflex/components/core/__init__.pyi +77 -37
- reflex/components/core/auto_scroll.pyi +7 -7
- reflex/components/core/banner.pyi +33 -33
- reflex/components/core/client_side_routing.py +7 -6
- reflex/components/core/client_side_routing.pyi +8 -59
- reflex/components/core/clipboard.pyi +7 -7
- reflex/components/core/debounce.py +1 -0
- reflex/components/core/debounce.pyi +7 -7
- reflex/components/core/foreach.py +5 -4
- reflex/components/core/helmet.py +14 -0
- reflex/components/{next/base.pyi → core/helmet.pyi} +12 -10
- reflex/components/core/html.pyi +7 -7
- reflex/components/core/match.py +3 -3
- reflex/components/core/sticky.pyi +21 -20
- reflex/components/core/upload.py +4 -2
- reflex/components/core/upload.pyi +26 -25
- reflex/components/datadisplay/__init__.pyi +13 -7
- reflex/components/datadisplay/code.py +14 -79
- reflex/components/datadisplay/code.pyi +11 -13
- reflex/components/datadisplay/dataeditor.pyi +38 -15
- reflex/components/datadisplay/shiki_code_block.py +5 -3
- reflex/components/datadisplay/shiki_code_block.pyi +16 -15
- reflex/components/dynamic.py +5 -5
- reflex/components/el/__init__.pyi +506 -246
- reflex/components/el/element.pyi +7 -7
- reflex/components/el/elements/__init__.pyi +504 -245
- reflex/components/el/elements/base.pyi +7 -7
- reflex/components/el/elements/forms.pyi +146 -101
- reflex/components/el/elements/inline.pyi +142 -142
- reflex/components/el/elements/media.pyi +131 -130
- reflex/components/el/elements/metadata.pyi +32 -32
- reflex/components/el/elements/other.pyi +37 -37
- reflex/components/el/elements/scripts.pyi +17 -17
- reflex/components/el/elements/sectioning.pyi +77 -77
- reflex/components/el/elements/tables.pyi +52 -52
- reflex/components/el/elements/typography.pyi +77 -77
- reflex/components/field.py +175 -0
- reflex/components/gridjs/datatable.py +2 -2
- reflex/components/gridjs/datatable.pyi +14 -14
- reflex/components/lucide/icon.py +6 -2
- reflex/components/lucide/icon.pyi +19 -17
- reflex/components/markdown/markdown.py +5 -3
- reflex/components/markdown/markdown.pyi +7 -7
- reflex/components/moment/moment.py +1 -1
- reflex/components/moment/moment.pyi +7 -7
- reflex/components/plotly/plotly.py +12 -6
- reflex/components/plotly/plotly.pyi +50 -49
- reflex/components/props.py +376 -27
- reflex/components/radix/__init__.pyi +123 -65
- reflex/components/radix/primitives/__init__.pyi +6 -4
- reflex/components/radix/primitives/accordion.py +8 -1
- reflex/components/radix/primitives/accordion.pyi +37 -37
- reflex/components/radix/primitives/base.pyi +12 -12
- reflex/components/radix/primitives/drawer.pyi +56 -55
- reflex/components/radix/primitives/form.pyi +63 -53
- reflex/components/radix/primitives/progress.pyi +26 -25
- reflex/components/radix/primitives/slider.pyi +27 -27
- reflex/components/radix/themes/__init__.pyi +5 -6
- reflex/components/radix/themes/base.py +3 -3
- reflex/components/radix/themes/base.pyi +42 -42
- reflex/components/radix/themes/color_mode.py +5 -6
- reflex/components/radix/themes/color_mode.pyi +17 -17
- reflex/components/radix/themes/components/__init__.pyi +75 -38
- reflex/components/radix/themes/components/alert_dialog.pyi +37 -37
- reflex/components/radix/themes/components/aspect_ratio.pyi +7 -7
- reflex/components/radix/themes/components/avatar.pyi +7 -7
- reflex/components/radix/themes/components/badge.pyi +7 -7
- reflex/components/radix/themes/components/button.pyi +7 -7
- reflex/components/radix/themes/components/callout.pyi +26 -25
- reflex/components/radix/themes/components/card.pyi +7 -7
- reflex/components/radix/themes/components/checkbox.pyi +16 -15
- reflex/components/radix/themes/components/checkbox_cards.pyi +12 -12
- reflex/components/radix/themes/components/checkbox_group.pyi +12 -12
- reflex/components/radix/themes/components/context_menu.pyi +67 -67
- reflex/components/radix/themes/components/data_list.pyi +22 -22
- reflex/components/radix/themes/components/dialog.pyi +36 -35
- reflex/components/radix/themes/components/dropdown_menu.pyi +42 -42
- reflex/components/radix/themes/components/hover_card.pyi +21 -20
- reflex/components/radix/themes/components/icon_button.pyi +7 -7
- reflex/components/radix/themes/components/inset.pyi +7 -7
- reflex/components/radix/themes/components/popover.pyi +22 -22
- reflex/components/radix/themes/components/progress.pyi +7 -7
- reflex/components/radix/themes/components/radio.pyi +7 -7
- reflex/components/radix/themes/components/radio_cards.pyi +12 -12
- reflex/components/radix/themes/components/radio_group.pyi +21 -20
- reflex/components/radix/themes/components/scroll_area.pyi +7 -7
- reflex/components/radix/themes/components/segmented_control.pyi +12 -12
- reflex/components/radix/themes/components/select.pyi +46 -45
- reflex/components/radix/themes/components/separator.pyi +7 -7
- reflex/components/radix/themes/components/skeleton.pyi +7 -7
- reflex/components/radix/themes/components/slider.pyi +17 -9
- reflex/components/radix/themes/components/spinner.pyi +7 -7
- reflex/components/radix/themes/components/switch.pyi +7 -7
- reflex/components/radix/themes/components/table.pyi +37 -37
- reflex/components/radix/themes/components/tabs.pyi +26 -25
- reflex/components/radix/themes/components/text_area.pyi +15 -9
- reflex/components/radix/themes/components/text_field.pyi +32 -19
- reflex/components/radix/themes/components/tooltip.pyi +7 -7
- reflex/components/radix/themes/layout/__init__.pyi +27 -14
- reflex/components/radix/themes/layout/base.pyi +7 -7
- reflex/components/radix/themes/layout/box.pyi +7 -7
- reflex/components/radix/themes/layout/center.pyi +7 -7
- reflex/components/radix/themes/layout/container.pyi +7 -7
- reflex/components/radix/themes/layout/flex.pyi +7 -7
- reflex/components/radix/themes/layout/grid.pyi +7 -7
- reflex/components/radix/themes/layout/list.pyi +26 -25
- reflex/components/radix/themes/layout/section.pyi +7 -7
- reflex/components/radix/themes/layout/spacer.pyi +7 -7
- reflex/components/radix/themes/layout/stack.pyi +17 -17
- reflex/components/radix/themes/typography/__init__.pyi +7 -5
- reflex/components/radix/themes/typography/blockquote.pyi +7 -7
- reflex/components/radix/themes/typography/code.pyi +7 -7
- reflex/components/radix/themes/typography/heading.pyi +7 -7
- reflex/components/radix/themes/typography/link.py +46 -11
- reflex/components/radix/themes/typography/link.pyi +312 -9
- reflex/components/radix/themes/typography/text.pyi +36 -35
- reflex/components/react_player/audio.pyi +10 -8
- reflex/components/react_player/react_player.pyi +7 -7
- reflex/components/react_player/video.pyi +10 -8
- reflex/components/recharts/__init__.pyi +208 -100
- reflex/components/recharts/cartesian.py +10 -8
- reflex/components/recharts/cartesian.pyi +90 -94
- reflex/components/recharts/charts.py +4 -2
- reflex/components/recharts/charts.pyi +49 -49
- reflex/components/recharts/general.pyi +31 -31
- reflex/components/recharts/polar.py +8 -4
- reflex/components/recharts/polar.pyi +23 -23
- reflex/components/recharts/recharts.py +2 -2
- reflex/components/recharts/recharts.pyi +12 -12
- reflex/components/sonner/toast.py +3 -3
- reflex/components/sonner/toast.pyi +9 -9
- reflex/config.py +10 -113
- reflex/constants/__init__.py +2 -2
- reflex/constants/base.py +28 -11
- reflex/constants/compiler.py +12 -3
- reflex/constants/event.py +1 -0
- reflex/constants/installer.py +26 -20
- reflex/constants/route.py +27 -8
- reflex/constants/state.py +2 -0
- reflex/custom_components/custom_components.py +0 -14
- reflex/environment.py +77 -5
- reflex/event.py +178 -81
- reflex/experimental/__init__.py +0 -30
- reflex/istate/__init__.py +69 -0
- reflex/istate/manager.py +1 -0
- reflex/istate/proxy.py +5 -3
- reflex/page.py +0 -27
- reflex/plugins/__init__.py +3 -2
- reflex/plugins/base.py +5 -1
- reflex/plugins/shared_tailwind.py +215 -0
- reflex/plugins/sitemap.py +206 -0
- reflex/plugins/tailwind_v3.py +15 -108
- reflex/plugins/tailwind_v4.py +18 -110
- reflex/reflex.py +1 -0
- reflex/route.py +157 -75
- reflex/state.py +171 -155
- reflex/testing.py +86 -16
- reflex/utils/build.py +38 -82
- reflex/utils/exec.py +83 -175
- reflex/utils/export.py +2 -2
- reflex/utils/format.py +1 -5
- reflex/utils/imports.py +5 -16
- reflex/utils/misc.py +67 -0
- reflex/utils/prerequisites.py +66 -68
- reflex/utils/processes.py +24 -47
- reflex/utils/pyi_generator.py +44 -49
- reflex/utils/serializers.py +14 -1
- reflex/utils/telemetry.py +0 -15
- reflex/utils/types.py +197 -62
- reflex/vars/__init__.py +2 -0
- reflex/vars/base.py +367 -134
- {reflex-0.7.14a6.dist-info → reflex-0.8.0.dist-info}/METADATA +15 -8
- reflex-0.8.0.dist-info/RECORD +403 -0
- reflex/.templates/web/next.config.js +0 -7
- reflex/components/base/head.py +0 -20
- reflex/components/base/head.pyi +0 -116
- reflex/components/next/__init__.py +0 -10
- reflex/components/next/base.py +0 -7
- reflex/components/next/image.py +0 -117
- reflex/components/next/image.pyi +0 -94
- reflex/components/next/link.py +0 -20
- reflex/components/next/link.pyi +0 -67
- reflex/components/next/video.py +0 -38
- reflex/components/next/video.pyi +0 -68
- reflex/components/suneditor/__init__.py +0 -5
- reflex/components/suneditor/editor.py +0 -269
- reflex/components/suneditor/editor.pyi +0 -199
- reflex/experimental/layout.py +0 -254
- reflex/experimental/layout.pyi +0 -814
- reflex-0.7.14a6.dist-info/RECORD +0 -408
- {reflex-0.7.14a6.dist-info → reflex-0.8.0.dist-info}/WHEEL +0 -0
- {reflex-0.7.14a6.dist-info → reflex-0.8.0.dist-info}/entry_points.txt +0 -0
- {reflex-0.7.14a6.dist-info → reflex-0.8.0.dist-info}/licenses/LICENSE +0 -0
reflex/vars/base.py
CHANGED
|
@@ -14,11 +14,14 @@ import re
|
|
|
14
14
|
import string
|
|
15
15
|
import uuid
|
|
16
16
|
import warnings
|
|
17
|
+
from abc import ABCMeta
|
|
17
18
|
from collections.abc import Callable, Coroutine, Iterable, Mapping, Sequence
|
|
19
|
+
from dataclasses import _MISSING_TYPE, MISSING
|
|
18
20
|
from decimal import Decimal
|
|
19
21
|
from types import CodeType, FunctionType
|
|
20
22
|
from typing import ( # noqa: UP035
|
|
21
23
|
TYPE_CHECKING,
|
|
24
|
+
Annotated,
|
|
22
25
|
Any,
|
|
23
26
|
ClassVar,
|
|
24
27
|
Dict,
|
|
@@ -40,11 +43,12 @@ from typing import ( # noqa: UP035
|
|
|
40
43
|
)
|
|
41
44
|
|
|
42
45
|
from rich.markup import escape
|
|
43
|
-
from typing_extensions import
|
|
46
|
+
from typing_extensions import dataclass_transform, override
|
|
44
47
|
|
|
45
48
|
from reflex import constants
|
|
46
49
|
from reflex.base import Base
|
|
47
50
|
from reflex.constants.compiler import Hooks
|
|
51
|
+
from reflex.constants.state import FIELD_MARKER
|
|
48
52
|
from reflex.utils import console, exceptions, imports, serializers, types
|
|
49
53
|
from reflex.utils.exceptions import (
|
|
50
54
|
ComputedVarSignatureError,
|
|
@@ -416,37 +420,6 @@ class Var(Generic[VAR_TYPE], metaclass=MetaclassVar):
|
|
|
416
420
|
"""
|
|
417
421
|
return False
|
|
418
422
|
|
|
419
|
-
@property
|
|
420
|
-
@deprecated("Use `_js_expr` instead.")
|
|
421
|
-
def _var_name(self) -> str:
|
|
422
|
-
"""The name of the var.
|
|
423
|
-
|
|
424
|
-
Returns:
|
|
425
|
-
The name of the var.
|
|
426
|
-
"""
|
|
427
|
-
return self._js_expr
|
|
428
|
-
|
|
429
|
-
@property
|
|
430
|
-
def _var_field_name(self) -> str:
|
|
431
|
-
"""The name of the field.
|
|
432
|
-
|
|
433
|
-
Returns:
|
|
434
|
-
The name of the field.
|
|
435
|
-
"""
|
|
436
|
-
var_data = self._get_all_var_data()
|
|
437
|
-
field_name = var_data.field_name if var_data else None
|
|
438
|
-
return field_name or self._js_expr
|
|
439
|
-
|
|
440
|
-
@property
|
|
441
|
-
@deprecated("Use `_js_expr` instead.")
|
|
442
|
-
def _var_name_unwrapped(self) -> str:
|
|
443
|
-
"""The name of the var without extra curly braces.
|
|
444
|
-
|
|
445
|
-
Returns:
|
|
446
|
-
The name of the var.
|
|
447
|
-
"""
|
|
448
|
-
return self._js_expr
|
|
449
|
-
|
|
450
423
|
@property
|
|
451
424
|
def _var_is_string(self) -> bool:
|
|
452
425
|
"""Whether the var is a string literal.
|
|
@@ -727,24 +700,6 @@ class Var(Generic[VAR_TYPE], metaclass=MetaclassVar):
|
|
|
727
700
|
|
|
728
701
|
return LiteralVar.create(value, _var_data=_var_data)
|
|
729
702
|
|
|
730
|
-
@classmethod
|
|
731
|
-
@deprecated("Use `.create()` instead.")
|
|
732
|
-
def create_safe(
|
|
733
|
-
cls,
|
|
734
|
-
*args: Any,
|
|
735
|
-
**kwargs: Any,
|
|
736
|
-
) -> Var:
|
|
737
|
-
"""Create a var from a value.
|
|
738
|
-
|
|
739
|
-
Args:
|
|
740
|
-
*args: The arguments to create the var from.
|
|
741
|
-
**kwargs: The keyword arguments to create the var from.
|
|
742
|
-
|
|
743
|
-
Returns:
|
|
744
|
-
The var.
|
|
745
|
-
"""
|
|
746
|
-
return cls.create(*args, **kwargs)
|
|
747
|
-
|
|
748
703
|
def __format__(self, format_spec: str) -> str:
|
|
749
704
|
"""Format the var into a Javascript equivalent to an f-string.
|
|
750
705
|
|
|
@@ -952,72 +907,29 @@ class Var(Generic[VAR_TYPE], metaclass=MetaclassVar):
|
|
|
952
907
|
|
|
953
908
|
return self
|
|
954
909
|
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
The default value of the var.
|
|
960
|
-
|
|
961
|
-
Raises:
|
|
962
|
-
ImportError: If the var is a dataframe and pandas is not installed.
|
|
963
|
-
"""
|
|
964
|
-
if types.is_optional(self._var_type):
|
|
965
|
-
return None
|
|
966
|
-
|
|
967
|
-
type_ = (
|
|
968
|
-
get_origin(self._var_type)
|
|
969
|
-
if types.is_generic_alias(self._var_type)
|
|
970
|
-
else self._var_type
|
|
971
|
-
)
|
|
972
|
-
if type_ is Literal:
|
|
973
|
-
args = get_args(self._var_type)
|
|
974
|
-
return args[0] if args else None
|
|
975
|
-
if safe_issubclass(type_, str):
|
|
976
|
-
return ""
|
|
977
|
-
if safe_issubclass(type_, types.get_args(int | float)):
|
|
978
|
-
return 0
|
|
979
|
-
if safe_issubclass(type_, bool):
|
|
980
|
-
return False
|
|
981
|
-
if safe_issubclass(type_, list):
|
|
982
|
-
return []
|
|
983
|
-
if safe_issubclass(type_, Mapping):
|
|
984
|
-
return {}
|
|
985
|
-
if safe_issubclass(type_, tuple):
|
|
986
|
-
return ()
|
|
987
|
-
if types.is_dataframe(type_):
|
|
988
|
-
try:
|
|
989
|
-
import pandas as pd
|
|
990
|
-
|
|
991
|
-
return pd.DataFrame()
|
|
992
|
-
except ImportError as e:
|
|
993
|
-
msg = "Please install pandas to use dataframes in your app."
|
|
994
|
-
raise ImportError(msg) from e
|
|
995
|
-
return set() if safe_issubclass(type_, set) else None
|
|
996
|
-
|
|
997
|
-
def _get_setter_name(self, include_state: bool = True) -> str:
|
|
910
|
+
@staticmethod
|
|
911
|
+
def _get_setter_name_for_name(
|
|
912
|
+
name: str,
|
|
913
|
+
) -> str:
|
|
998
914
|
"""Get the name of the var's generated setter function.
|
|
999
915
|
|
|
1000
916
|
Args:
|
|
1001
|
-
|
|
917
|
+
name: The name of the var.
|
|
1002
918
|
|
|
1003
919
|
Returns:
|
|
1004
920
|
The name of the setter function.
|
|
1005
921
|
"""
|
|
1006
|
-
|
|
1007
|
-
var_data = self._get_all_var_data()
|
|
1008
|
-
if var_data is None:
|
|
1009
|
-
return setter
|
|
1010
|
-
if not include_state or var_data.state == "":
|
|
1011
|
-
return setter
|
|
1012
|
-
return var_data.state + "." + setter
|
|
922
|
+
return constants.SETTER_PREFIX + name
|
|
1013
923
|
|
|
1014
|
-
def _get_setter(self) -> Callable[[BaseState, Any], None]:
|
|
924
|
+
def _get_setter(self, name: str) -> Callable[[BaseState, Any], None]:
|
|
1015
925
|
"""Get the var's setter function.
|
|
1016
926
|
|
|
927
|
+
Args:
|
|
928
|
+
name: The name of the var.
|
|
929
|
+
|
|
1017
930
|
Returns:
|
|
1018
931
|
A function that that creates a setter for the var.
|
|
1019
932
|
"""
|
|
1020
|
-
actual_name = self._var_field_name
|
|
1021
933
|
|
|
1022
934
|
def setter(state: Any, value: Any):
|
|
1023
935
|
"""Get the setter for the var.
|
|
@@ -1029,17 +941,17 @@ class Var(Generic[VAR_TYPE], metaclass=MetaclassVar):
|
|
|
1029
941
|
if self._var_type in [int, float]:
|
|
1030
942
|
try:
|
|
1031
943
|
value = self._var_type(value)
|
|
1032
|
-
setattr(state,
|
|
944
|
+
setattr(state, name, value)
|
|
1033
945
|
except ValueError:
|
|
1034
946
|
console.debug(
|
|
1035
947
|
f"{type(state).__name__}.{self._js_expr}: Failed conversion of {value!s} to '{self._var_type.__name__}'. Value not set.",
|
|
1036
948
|
)
|
|
1037
949
|
else:
|
|
1038
|
-
setattr(state,
|
|
950
|
+
setattr(state, name, value)
|
|
1039
951
|
|
|
1040
952
|
setter.__annotations__["value"] = self._var_type
|
|
1041
953
|
|
|
1042
|
-
setter.__qualname__ =
|
|
954
|
+
setter.__qualname__ = Var._get_setter_name_for_name(name)
|
|
1043
955
|
|
|
1044
956
|
return setter
|
|
1045
957
|
|
|
@@ -1218,18 +1130,6 @@ class Var(Generic[VAR_TYPE], metaclass=MetaclassVar):
|
|
|
1218
1130
|
).to(ObjectVar, Mapping[str, str])
|
|
1219
1131
|
return refs[LiteralVar.create(str(self))]
|
|
1220
1132
|
|
|
1221
|
-
@deprecated("Use `.js_type()` instead.")
|
|
1222
|
-
def _type(self) -> StringVar:
|
|
1223
|
-
"""Returns the type of the object.
|
|
1224
|
-
|
|
1225
|
-
This method uses the `typeof` function from the `FunctionStringVar` class
|
|
1226
|
-
to determine the type of the object.
|
|
1227
|
-
|
|
1228
|
-
Returns:
|
|
1229
|
-
StringVar: A string variable representing the type of the object.
|
|
1230
|
-
"""
|
|
1231
|
-
return self.js_type()
|
|
1232
|
-
|
|
1233
1133
|
def js_type(self) -> StringVar:
|
|
1234
1134
|
"""Returns the javascript type of the object.
|
|
1235
1135
|
|
|
@@ -2111,6 +2011,8 @@ class ComputedVar(Var[RETURN_TYPE]):
|
|
|
2111
2011
|
default_factory=lambda: lambda _: None
|
|
2112
2012
|
) # pyright: ignore [reportAssignmentType]
|
|
2113
2013
|
|
|
2014
|
+
_name: str = dataclasses.field(default="")
|
|
2015
|
+
|
|
2114
2016
|
def __init__(
|
|
2115
2017
|
self,
|
|
2116
2018
|
fget: Callable[[BASE_STATE], RETURN_TYPE],
|
|
@@ -2144,14 +2046,18 @@ class ComputedVar(Var[RETURN_TYPE]):
|
|
|
2144
2046
|
|
|
2145
2047
|
if hint is Any:
|
|
2146
2048
|
raise UntypedComputedVarError(var_name=fget.__name__)
|
|
2147
|
-
|
|
2049
|
+
is_using_fget_name = "_js_expr" not in kwargs
|
|
2050
|
+
js_expr = kwargs.pop("_js_expr", fget.__name__ + FIELD_MARKER)
|
|
2148
2051
|
kwargs.setdefault("_var_type", hint)
|
|
2149
2052
|
|
|
2150
2053
|
Var.__init__(
|
|
2151
2054
|
self,
|
|
2152
|
-
_js_expr=
|
|
2055
|
+
_js_expr=js_expr,
|
|
2153
2056
|
_var_type=kwargs.pop("_var_type"),
|
|
2154
|
-
_var_data=kwargs.pop(
|
|
2057
|
+
_var_data=kwargs.pop(
|
|
2058
|
+
"_var_data",
|
|
2059
|
+
VarData(field_name=fget.__name__) if is_using_fget_name else None,
|
|
2060
|
+
),
|
|
2155
2061
|
)
|
|
2156
2062
|
|
|
2157
2063
|
if kwargs:
|
|
@@ -2164,6 +2070,7 @@ class ComputedVar(Var[RETURN_TYPE]):
|
|
|
2164
2070
|
object.__setattr__(self, "_backend", backend)
|
|
2165
2071
|
object.__setattr__(self, "_initial_value", initial_value)
|
|
2166
2072
|
object.__setattr__(self, "_cache", cache)
|
|
2073
|
+
object.__setattr__(self, "_name", fget.__name__)
|
|
2167
2074
|
|
|
2168
2075
|
if isinstance(interval, int):
|
|
2169
2076
|
interval = datetime.timedelta(seconds=interval)
|
|
@@ -2395,7 +2302,7 @@ class ComputedVar(Var[RETURN_TYPE]):
|
|
|
2395
2302
|
"""
|
|
2396
2303
|
if instance is None:
|
|
2397
2304
|
state_where_defined = owner
|
|
2398
|
-
while self.
|
|
2305
|
+
while self._name in state_where_defined.inherited_vars:
|
|
2399
2306
|
state_where_defined = state_where_defined.get_parent_state()
|
|
2400
2307
|
|
|
2401
2308
|
field_name = (
|
|
@@ -2406,7 +2313,7 @@ class ComputedVar(Var[RETURN_TYPE]):
|
|
|
2406
2313
|
|
|
2407
2314
|
return dispatch(
|
|
2408
2315
|
field_name,
|
|
2409
|
-
var_data=VarData.from_state(state_where_defined, self.
|
|
2316
|
+
var_data=VarData.from_state(state_where_defined, self._name),
|
|
2410
2317
|
result_var_type=self._var_type,
|
|
2411
2318
|
existing_var=self,
|
|
2412
2319
|
)
|
|
@@ -2518,7 +2425,7 @@ class ComputedVar(Var[RETURN_TYPE]):
|
|
|
2518
2425
|
objclass.get_root_state().get_class_substate(
|
|
2519
2426
|
state_name
|
|
2520
2427
|
)._var_dependencies.setdefault(var_name, set()).add(
|
|
2521
|
-
(objclass.get_full_name(), self.
|
|
2428
|
+
(objclass.get_full_name(), self._name)
|
|
2522
2429
|
)
|
|
2523
2430
|
return
|
|
2524
2431
|
msg = (
|
|
@@ -3382,19 +3289,106 @@ if TYPE_CHECKING:
|
|
|
3382
3289
|
MAPPING_TYPE = TypeVar("MAPPING_TYPE", bound=Mapping | None)
|
|
3383
3290
|
V = TypeVar("V")
|
|
3384
3291
|
|
|
3292
|
+
|
|
3385
3293
|
FIELD_TYPE = TypeVar("FIELD_TYPE")
|
|
3386
3294
|
|
|
3387
3295
|
|
|
3388
3296
|
class Field(Generic[FIELD_TYPE]):
|
|
3389
|
-
"""
|
|
3297
|
+
"""A field for a state."""
|
|
3390
3298
|
|
|
3391
|
-
|
|
3392
|
-
|
|
3299
|
+
if TYPE_CHECKING:
|
|
3300
|
+
type_: GenericType
|
|
3301
|
+
default: FIELD_TYPE | _MISSING_TYPE
|
|
3302
|
+
default_factory: Callable[[], FIELD_TYPE] | None
|
|
3303
|
+
|
|
3304
|
+
def __init__(
|
|
3305
|
+
self,
|
|
3306
|
+
default: FIELD_TYPE | _MISSING_TYPE = MISSING,
|
|
3307
|
+
default_factory: Callable[[], FIELD_TYPE] | None = None,
|
|
3308
|
+
is_var: bool = True,
|
|
3309
|
+
annotated_type: GenericType # pyright: ignore [reportRedeclaration]
|
|
3310
|
+
| _MISSING_TYPE = MISSING,
|
|
3311
|
+
) -> None:
|
|
3312
|
+
"""Initialize the field.
|
|
3393
3313
|
|
|
3394
3314
|
Args:
|
|
3395
|
-
|
|
3396
|
-
|
|
3315
|
+
default: The default value for the field.
|
|
3316
|
+
default_factory: The default factory for the field.
|
|
3317
|
+
is_var: Whether the field is a Var.
|
|
3318
|
+
annotated_type: The annotated type for the field.
|
|
3319
|
+
"""
|
|
3320
|
+
self.default = default
|
|
3321
|
+
self.default_factory = default_factory
|
|
3322
|
+
self.is_var = is_var
|
|
3323
|
+
if annotated_type is not MISSING:
|
|
3324
|
+
type_origin = get_origin(annotated_type) or annotated_type
|
|
3325
|
+
if type_origin is Field and (
|
|
3326
|
+
args := getattr(annotated_type, "__args__", None)
|
|
3327
|
+
):
|
|
3328
|
+
annotated_type: GenericType = args[0]
|
|
3329
|
+
type_origin = get_origin(annotated_type) or annotated_type
|
|
3330
|
+
|
|
3331
|
+
if self.default is MISSING and self.default_factory is None:
|
|
3332
|
+
default_value = types.get_default_value_for_type(annotated_type)
|
|
3333
|
+
if default_value is None and not types.is_optional(annotated_type):
|
|
3334
|
+
annotated_type = annotated_type | None
|
|
3335
|
+
if types.is_immutable(default_value):
|
|
3336
|
+
self.default = default_value
|
|
3337
|
+
else:
|
|
3338
|
+
self.default_factory = functools.partial(
|
|
3339
|
+
copy.deepcopy, default_value
|
|
3340
|
+
)
|
|
3341
|
+
self.outer_type_ = self.annotated_type = annotated_type
|
|
3342
|
+
|
|
3343
|
+
if type_origin is Annotated:
|
|
3344
|
+
type_origin = annotated_type.__origin__ # pyright: ignore [reportAttributeAccessIssue]
|
|
3345
|
+
|
|
3346
|
+
self.type_ = self.type_origin = type_origin
|
|
3347
|
+
else:
|
|
3348
|
+
self.outer_type_ = self.annotated_type = self.type_ = self.type_origin = Any
|
|
3349
|
+
|
|
3350
|
+
def default_value(self) -> FIELD_TYPE:
|
|
3351
|
+
"""Get the default value for the field.
|
|
3352
|
+
|
|
3353
|
+
Returns:
|
|
3354
|
+
The default value for the field.
|
|
3355
|
+
|
|
3356
|
+
Raises:
|
|
3357
|
+
ValueError: If no default value or factory is provided.
|
|
3358
|
+
"""
|
|
3359
|
+
if self.default is not MISSING:
|
|
3360
|
+
return self.default
|
|
3361
|
+
if self.default_factory is not None:
|
|
3362
|
+
return self.default_factory()
|
|
3363
|
+
msg = "No default value or factory provided."
|
|
3364
|
+
raise ValueError(msg)
|
|
3365
|
+
|
|
3366
|
+
def __repr__(self) -> str:
|
|
3367
|
+
"""Represent the field in a readable format.
|
|
3368
|
+
|
|
3369
|
+
Returns:
|
|
3370
|
+
The string representation of the field.
|
|
3397
3371
|
"""
|
|
3372
|
+
annotated_type_str = (
|
|
3373
|
+
f", annotated_type={self.annotated_type!r}"
|
|
3374
|
+
if self.annotated_type is not MISSING
|
|
3375
|
+
else ""
|
|
3376
|
+
)
|
|
3377
|
+
if self.default is not MISSING:
|
|
3378
|
+
return f"Field(default={self.default!r}, is_var={self.is_var}{annotated_type_str})"
|
|
3379
|
+
return f"Field(default_factory={self.default_factory!r}, is_var={self.is_var}{annotated_type_str})"
|
|
3380
|
+
|
|
3381
|
+
if TYPE_CHECKING:
|
|
3382
|
+
|
|
3383
|
+
def __set__(self, instance: Any, value: FIELD_TYPE):
|
|
3384
|
+
"""Set the Var.
|
|
3385
|
+
|
|
3386
|
+
Args:
|
|
3387
|
+
instance: The instance of the class setting the Var.
|
|
3388
|
+
value: The value to set the Var to.
|
|
3389
|
+
|
|
3390
|
+
# noqa: DAR101 self
|
|
3391
|
+
"""
|
|
3398
3392
|
|
|
3399
3393
|
@overload
|
|
3400
3394
|
def __get__(self: Field[None], instance: None, owner: Any) -> NoneVar: ...
|
|
@@ -3484,13 +3478,252 @@ class Field(Generic[FIELD_TYPE]):
|
|
|
3484
3478
|
"""
|
|
3485
3479
|
|
|
3486
3480
|
|
|
3487
|
-
|
|
3488
|
-
|
|
3481
|
+
@overload
|
|
3482
|
+
def field(
|
|
3483
|
+
default: FIELD_TYPE | _MISSING_TYPE = MISSING,
|
|
3484
|
+
*,
|
|
3485
|
+
is_var: Literal[False],
|
|
3486
|
+
default_factory: Callable[[], FIELD_TYPE] | None = None,
|
|
3487
|
+
) -> FIELD_TYPE: ...
|
|
3488
|
+
|
|
3489
|
+
|
|
3490
|
+
@overload
|
|
3491
|
+
def field(
|
|
3492
|
+
default: FIELD_TYPE | _MISSING_TYPE = MISSING,
|
|
3493
|
+
*,
|
|
3494
|
+
default_factory: Callable[[], FIELD_TYPE] | None = None,
|
|
3495
|
+
is_var: Literal[True] = True,
|
|
3496
|
+
) -> Field[FIELD_TYPE]: ...
|
|
3497
|
+
|
|
3498
|
+
|
|
3499
|
+
def field(
|
|
3500
|
+
default: FIELD_TYPE | _MISSING_TYPE = MISSING,
|
|
3501
|
+
*,
|
|
3502
|
+
default_factory: Callable[[], FIELD_TYPE] | None = None,
|
|
3503
|
+
is_var: bool = True,
|
|
3504
|
+
) -> Field[FIELD_TYPE] | FIELD_TYPE:
|
|
3505
|
+
"""Create a field for a state.
|
|
3489
3506
|
|
|
3490
3507
|
Args:
|
|
3491
|
-
|
|
3508
|
+
default: The default value for the field.
|
|
3509
|
+
default_factory: The default factory for the field.
|
|
3510
|
+
is_var: Whether the field is a Var.
|
|
3492
3511
|
|
|
3493
3512
|
Returns:
|
|
3494
|
-
The
|
|
3513
|
+
The field for the state.
|
|
3514
|
+
|
|
3515
|
+
Raises:
|
|
3516
|
+
ValueError: If both default and default_factory are specified.
|
|
3495
3517
|
"""
|
|
3496
|
-
|
|
3518
|
+
if default is not MISSING and default_factory is not None:
|
|
3519
|
+
msg = "cannot specify both default and default_factory"
|
|
3520
|
+
raise ValueError(msg)
|
|
3521
|
+
if default is not MISSING and not types.is_immutable(default):
|
|
3522
|
+
console.warn(
|
|
3523
|
+
"Mutable default values are not recommended. "
|
|
3524
|
+
"Use default_factory instead to avoid unexpected behavior."
|
|
3525
|
+
)
|
|
3526
|
+
return Field(
|
|
3527
|
+
default_factory=functools.partial(copy.deepcopy, default),
|
|
3528
|
+
is_var=is_var,
|
|
3529
|
+
)
|
|
3530
|
+
return Field(
|
|
3531
|
+
default=default,
|
|
3532
|
+
default_factory=default_factory,
|
|
3533
|
+
is_var=is_var,
|
|
3534
|
+
)
|
|
3535
|
+
|
|
3536
|
+
|
|
3537
|
+
@dataclass_transform(kw_only_default=True, field_specifiers=(field,))
|
|
3538
|
+
class BaseStateMeta(ABCMeta):
|
|
3539
|
+
"""Meta class for BaseState."""
|
|
3540
|
+
|
|
3541
|
+
if TYPE_CHECKING:
|
|
3542
|
+
__inherited_fields__: Mapping[str, Field]
|
|
3543
|
+
__own_fields__: dict[str, Field]
|
|
3544
|
+
__fields__: dict[str, Field]
|
|
3545
|
+
|
|
3546
|
+
# Whether this state class is a mixin and should not be instantiated.
|
|
3547
|
+
_mixin: bool = False
|
|
3548
|
+
|
|
3549
|
+
def __new__(
|
|
3550
|
+
cls,
|
|
3551
|
+
name: str,
|
|
3552
|
+
bases: tuple[type],
|
|
3553
|
+
namespace: dict[str, Any],
|
|
3554
|
+
mixin: bool = False,
|
|
3555
|
+
) -> type:
|
|
3556
|
+
"""Create a new class.
|
|
3557
|
+
|
|
3558
|
+
Args:
|
|
3559
|
+
name: The name of the class.
|
|
3560
|
+
bases: The bases of the class.
|
|
3561
|
+
namespace: The namespace of the class.
|
|
3562
|
+
mixin: Whether the class is a mixin and should not be instantiated.
|
|
3563
|
+
|
|
3564
|
+
Returns:
|
|
3565
|
+
The new class.
|
|
3566
|
+
"""
|
|
3567
|
+
state_bases = [
|
|
3568
|
+
base for base in bases if issubclass(base, EvenMoreBasicBaseState)
|
|
3569
|
+
]
|
|
3570
|
+
mixin = mixin or (
|
|
3571
|
+
bool(state_bases) and all(base._mixin for base in state_bases)
|
|
3572
|
+
)
|
|
3573
|
+
# Add the field to the class
|
|
3574
|
+
inherited_fields: dict[str, Field] = {}
|
|
3575
|
+
own_fields: dict[str, Field] = {}
|
|
3576
|
+
resolved_annotations = types.resolve_annotations(
|
|
3577
|
+
namespace.get("__annotations__", {}), namespace["__module__"]
|
|
3578
|
+
)
|
|
3579
|
+
|
|
3580
|
+
for base in bases[::-1]:
|
|
3581
|
+
if hasattr(base, "__inherited_fields__"):
|
|
3582
|
+
inherited_fields.update(base.__inherited_fields__)
|
|
3583
|
+
for base in bases[::-1]:
|
|
3584
|
+
if hasattr(base, "__own_fields__"):
|
|
3585
|
+
inherited_fields.update(base.__own_fields__)
|
|
3586
|
+
|
|
3587
|
+
for key, value in [
|
|
3588
|
+
(key, value)
|
|
3589
|
+
for key, value in namespace.items()
|
|
3590
|
+
if key not in resolved_annotations
|
|
3591
|
+
]:
|
|
3592
|
+
if isinstance(value, Field):
|
|
3593
|
+
if value.annotated_type is not Any:
|
|
3594
|
+
new_value = value
|
|
3595
|
+
elif value.default is not MISSING:
|
|
3596
|
+
new_value = Field(
|
|
3597
|
+
default=value.default,
|
|
3598
|
+
is_var=value.is_var,
|
|
3599
|
+
annotated_type=figure_out_type(value.default),
|
|
3600
|
+
)
|
|
3601
|
+
else:
|
|
3602
|
+
new_value = Field(
|
|
3603
|
+
default_factory=value.default_factory,
|
|
3604
|
+
is_var=value.is_var,
|
|
3605
|
+
annotated_type=Any,
|
|
3606
|
+
)
|
|
3607
|
+
elif (
|
|
3608
|
+
not key.startswith("__")
|
|
3609
|
+
and not callable(value)
|
|
3610
|
+
and not isinstance(value, (staticmethod, classmethod, property, Var))
|
|
3611
|
+
):
|
|
3612
|
+
if types.is_immutable(value):
|
|
3613
|
+
new_value = Field(
|
|
3614
|
+
default=value,
|
|
3615
|
+
annotated_type=figure_out_type(value),
|
|
3616
|
+
)
|
|
3617
|
+
else:
|
|
3618
|
+
new_value = Field(
|
|
3619
|
+
default_factory=functools.partial(copy.deepcopy, value),
|
|
3620
|
+
annotated_type=figure_out_type(value),
|
|
3621
|
+
)
|
|
3622
|
+
else:
|
|
3623
|
+
continue
|
|
3624
|
+
|
|
3625
|
+
own_fields[key] = new_value
|
|
3626
|
+
|
|
3627
|
+
for key, annotation in resolved_annotations.items():
|
|
3628
|
+
value = namespace.get(key, MISSING)
|
|
3629
|
+
|
|
3630
|
+
if types.is_classvar(annotation):
|
|
3631
|
+
# If the annotation is a classvar, skip it.
|
|
3632
|
+
continue
|
|
3633
|
+
|
|
3634
|
+
if value is MISSING:
|
|
3635
|
+
value = Field(
|
|
3636
|
+
annotated_type=annotation,
|
|
3637
|
+
)
|
|
3638
|
+
elif not isinstance(value, Field):
|
|
3639
|
+
if types.is_immutable(value):
|
|
3640
|
+
value = Field(
|
|
3641
|
+
default=value,
|
|
3642
|
+
annotated_type=annotation,
|
|
3643
|
+
)
|
|
3644
|
+
else:
|
|
3645
|
+
value = Field(
|
|
3646
|
+
default_factory=functools.partial(copy.deepcopy, value),
|
|
3647
|
+
annotated_type=annotation,
|
|
3648
|
+
)
|
|
3649
|
+
else:
|
|
3650
|
+
value = Field(
|
|
3651
|
+
default=value.default,
|
|
3652
|
+
default_factory=value.default_factory,
|
|
3653
|
+
is_var=value.is_var,
|
|
3654
|
+
annotated_type=annotation,
|
|
3655
|
+
)
|
|
3656
|
+
|
|
3657
|
+
own_fields[key] = value
|
|
3658
|
+
|
|
3659
|
+
namespace["__own_fields__"] = own_fields
|
|
3660
|
+
namespace["__inherited_fields__"] = inherited_fields
|
|
3661
|
+
namespace["__fields__"] = inherited_fields | own_fields
|
|
3662
|
+
namespace["_mixin"] = mixin
|
|
3663
|
+
return super().__new__(cls, name, bases, namespace)
|
|
3664
|
+
|
|
3665
|
+
|
|
3666
|
+
class EvenMoreBasicBaseState(metaclass=BaseStateMeta):
|
|
3667
|
+
"""A simplified base state class that provides basic functionality."""
|
|
3668
|
+
|
|
3669
|
+
def __init__(
|
|
3670
|
+
self,
|
|
3671
|
+
**kwargs,
|
|
3672
|
+
):
|
|
3673
|
+
"""Initialize the state with the given kwargs.
|
|
3674
|
+
|
|
3675
|
+
Args:
|
|
3676
|
+
**kwargs: The kwargs to pass to the state.
|
|
3677
|
+
"""
|
|
3678
|
+
super().__init__()
|
|
3679
|
+
for key, value in kwargs.items():
|
|
3680
|
+
object.__setattr__(self, key, value)
|
|
3681
|
+
for name, value in type(self).get_fields().items():
|
|
3682
|
+
if name not in kwargs:
|
|
3683
|
+
default_value = value.default_value()
|
|
3684
|
+
object.__setattr__(self, name, default_value)
|
|
3685
|
+
|
|
3686
|
+
def set(self, **kwargs):
|
|
3687
|
+
"""Mutate the state by setting the given kwargs. Returns the state.
|
|
3688
|
+
|
|
3689
|
+
Args:
|
|
3690
|
+
**kwargs: The kwargs to set.
|
|
3691
|
+
|
|
3692
|
+
Returns:
|
|
3693
|
+
The state with the fields set to the given kwargs.
|
|
3694
|
+
"""
|
|
3695
|
+
for key, value in kwargs.items():
|
|
3696
|
+
setattr(self, key, value)
|
|
3697
|
+
return self
|
|
3698
|
+
|
|
3699
|
+
@classmethod
|
|
3700
|
+
def get_fields(cls) -> Mapping[str, Field]:
|
|
3701
|
+
"""Get the fields of the component.
|
|
3702
|
+
|
|
3703
|
+
Returns:
|
|
3704
|
+
The fields of the component.
|
|
3705
|
+
"""
|
|
3706
|
+
return cls.__fields__
|
|
3707
|
+
|
|
3708
|
+
@classmethod
|
|
3709
|
+
def add_field(cls, name: str, var: Var, default_value: Any):
|
|
3710
|
+
"""Add a field to the class after class definition.
|
|
3711
|
+
|
|
3712
|
+
Used by State.add_var() to correctly handle the new variable.
|
|
3713
|
+
|
|
3714
|
+
Args:
|
|
3715
|
+
name: The name of the field to add.
|
|
3716
|
+
var: The variable to add a field for.
|
|
3717
|
+
default_value: The default value of the field.
|
|
3718
|
+
"""
|
|
3719
|
+
if types.is_immutable(default_value):
|
|
3720
|
+
new_field = Field(
|
|
3721
|
+
default=default_value,
|
|
3722
|
+
annotated_type=var._var_type,
|
|
3723
|
+
)
|
|
3724
|
+
else:
|
|
3725
|
+
new_field = Field(
|
|
3726
|
+
default_factory=functools.partial(copy.deepcopy, default_value),
|
|
3727
|
+
annotated_type=var._var_type,
|
|
3728
|
+
)
|
|
3729
|
+
cls.__fields__[name] = new_field
|