reflex 0.6.4a3__py3-none-any.whl → 0.6.5a1__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.
- reflex/.templates/jinja/web/pages/custom_component.js.jinja2 +0 -14
- reflex/.templates/jinja/web/pages/utils.js.jinja2 +4 -8
- reflex/.templates/web/components/shiki/code.js +16 -11
- reflex/.templates/web/utils/state.js +29 -21
- reflex/__init__.py +4 -0
- reflex/__init__.pyi +4 -0
- reflex/app.py +148 -154
- reflex/app_mixins/lifespan.py +5 -1
- reflex/app_mixins/middleware.py +3 -1
- reflex/app_mixins/mixin.py +3 -2
- reflex/base.py +2 -4
- reflex/compiler/compiler.py +111 -37
- reflex/components/base/app_wrap.pyi +17 -17
- reflex/components/base/bare.py +72 -3
- reflex/components/base/body.pyi +17 -17
- reflex/components/base/document.pyi +81 -81
- reflex/components/base/error_boundary.pyi +25 -18
- reflex/components/base/fragment.pyi +17 -17
- reflex/components/base/head.pyi +33 -33
- reflex/components/base/link.pyi +33 -33
- reflex/components/base/meta.pyi +65 -65
- reflex/components/base/script.py +4 -4
- reflex/components/base/script.pyi +23 -20
- reflex/components/component.py +250 -31
- reflex/components/core/banner.py +1 -1
- reflex/components/core/banner.pyi +81 -81
- reflex/components/core/client_side_routing.pyi +33 -33
- reflex/components/core/clipboard.py +2 -2
- reflex/components/core/clipboard.pyi +24 -18
- reflex/components/core/debounce.py +2 -2
- reflex/components/core/debounce.pyi +18 -18
- reflex/components/core/html.pyi +17 -17
- reflex/components/core/upload.py +82 -28
- reflex/components/core/upload.pyi +77 -72
- reflex/components/datadisplay/code.py +55 -40
- reflex/components/datadisplay/code.pyi +46 -44
- reflex/components/datadisplay/dataeditor.py +21 -20
- reflex/components/datadisplay/dataeditor.pyi +103 -35
- reflex/components/datadisplay/shiki_code_block.py +60 -27
- reflex/components/datadisplay/shiki_code_block.pyi +86 -65
- reflex/components/dynamic.py +9 -5
- reflex/components/el/element.pyi +17 -17
- reflex/components/el/elements/base.pyi +17 -17
- reflex/components/el/elements/forms.py +12 -3
- reflex/components/el/elements/forms.pyi +293 -233
- reflex/components/el/elements/inline.pyi +449 -449
- reflex/components/el/elements/media.pyi +401 -401
- reflex/components/el/elements/metadata.pyi +97 -97
- reflex/components/el/elements/other.pyi +113 -113
- reflex/components/el/elements/scripts.pyi +49 -49
- reflex/components/el/elements/sectioning.pyi +241 -241
- reflex/components/el/elements/tables.pyi +161 -161
- reflex/components/el/elements/typography.pyi +241 -241
- reflex/components/gridjs/datatable.pyi +33 -33
- reflex/components/lucide/icon.py +1 -1
- reflex/components/lucide/icon.pyi +33 -33
- reflex/components/markdown/markdown.py +180 -49
- reflex/components/markdown/markdown.pyi +36 -19
- reflex/components/moment/moment.py +17 -21
- reflex/components/moment/moment.pyi +26 -21
- reflex/components/next/base.pyi +17 -17
- reflex/components/next/image.py +3 -3
- reflex/components/next/image.pyi +21 -19
- reflex/components/next/link.pyi +17 -17
- reflex/components/next/video.pyi +17 -17
- reflex/components/plotly/plotly.py +79 -78
- reflex/components/plotly/plotly.pyi +91 -41
- reflex/components/props.py +34 -0
- reflex/components/radix/primitives/accordion.py +15 -8
- reflex/components/radix/primitives/accordion.pyi +121 -118
- reflex/components/radix/primitives/base.pyi +33 -33
- reflex/components/radix/primitives/drawer.py +41 -20
- reflex/components/radix/primitives/drawer.pyi +279 -190
- reflex/components/radix/primitives/form.py +2 -2
- reflex/components/radix/primitives/form.pyi +200 -167
- reflex/components/radix/primitives/progress.pyi +81 -81
- reflex/components/radix/primitives/slider.pyi +89 -83
- reflex/components/radix/themes/base.py +27 -1
- reflex/components/radix/themes/base.pyi +286 -113
- reflex/components/radix/themes/color_mode.py +17 -9
- reflex/components/radix/themes/color_mode.pyi +68 -56
- reflex/components/radix/themes/components/alert_dialog.py +8 -5
- reflex/components/radix/themes/components/alert_dialog.pyi +125 -117
- reflex/components/radix/themes/components/aspect_ratio.pyi +17 -17
- reflex/components/radix/themes/components/avatar.py +1 -5
- reflex/components/radix/themes/components/avatar.pyi +17 -17
- reflex/components/radix/themes/components/badge.py +1 -5
- reflex/components/radix/themes/components/badge.pyi +17 -17
- reflex/components/radix/themes/components/button.pyi +18 -21
- reflex/components/radix/themes/components/callout.py +1 -4
- reflex/components/radix/themes/components/callout.pyi +81 -81
- reflex/components/radix/themes/components/card.py +1 -3
- reflex/components/radix/themes/components/card.pyi +17 -17
- reflex/components/radix/themes/components/checkbox.py +4 -8
- reflex/components/radix/themes/components/checkbox.pyi +61 -52
- reflex/components/radix/themes/components/checkbox_cards.pyi +33 -33
- reflex/components/radix/themes/components/checkbox_group.pyi +33 -33
- reflex/components/radix/themes/components/context_menu.py +121 -28
- reflex/components/radix/themes/components/context_menu.pyi +250 -147
- reflex/components/radix/themes/components/data_list.pyi +65 -65
- reflex/components/radix/themes/components/dialog.py +11 -11
- reflex/components/radix/themes/components/dialog.pyi +135 -120
- reflex/components/radix/themes/components/dropdown_menu.py +14 -25
- reflex/components/radix/themes/components/dropdown_menu.pyi +157 -145
- reflex/components/radix/themes/components/hover_card.py +19 -7
- reflex/components/radix/themes/components/hover_card.pyi +102 -67
- reflex/components/radix/themes/components/icon_button.pyi +18 -21
- reflex/components/radix/themes/components/inset.py +1 -3
- reflex/components/radix/themes/components/inset.pyi +17 -17
- reflex/components/radix/themes/components/popover.py +22 -13
- reflex/components/radix/themes/components/popover.pyi +98 -72
- reflex/components/radix/themes/components/progress.pyi +17 -17
- reflex/components/radix/themes/components/radio.pyi +17 -17
- reflex/components/radix/themes/components/radio_cards.py +2 -2
- reflex/components/radix/themes/components/radio_cards.pyi +37 -34
- reflex/components/radix/themes/components/radio_group.py +3 -7
- reflex/components/radix/themes/components/radio_group.pyi +69 -66
- reflex/components/radix/themes/components/scroll_area.py +1 -3
- reflex/components/radix/themes/components/scroll_area.pyi +17 -17
- reflex/components/radix/themes/components/segmented_control.pyi +37 -34
- reflex/components/radix/themes/components/select.py +7 -11
- reflex/components/radix/themes/components/select.pyi +175 -154
- reflex/components/radix/themes/components/separator.py +1 -4
- reflex/components/radix/themes/components/separator.pyi +17 -17
- reflex/components/radix/themes/components/skeleton.pyi +17 -17
- reflex/components/radix/themes/components/slider.py +12 -21
- reflex/components/radix/themes/components/slider.pyi +47 -25
- reflex/components/radix/themes/components/spinner.py +1 -4
- reflex/components/radix/themes/components/spinner.pyi +17 -17
- reflex/components/radix/themes/components/switch.py +3 -6
- reflex/components/radix/themes/components/switch.pyi +21 -18
- reflex/components/radix/themes/components/table.py +21 -5
- reflex/components/radix/themes/components/table.pyi +392 -116
- reflex/components/radix/themes/components/tabs.py +3 -6
- reflex/components/radix/themes/components/tabs.pyi +89 -83
- reflex/components/radix/themes/components/text_area.py +1 -5
- reflex/components/radix/themes/components/text_area.pyi +43 -20
- reflex/components/radix/themes/components/text_field.py +1 -5
- reflex/components/radix/themes/components/text_field.pyi +101 -55
- reflex/components/radix/themes/components/tooltip.py +5 -7
- reflex/components/radix/themes/components/tooltip.pyi +25 -22
- reflex/components/radix/themes/layout/base.py +2 -27
- reflex/components/radix/themes/layout/base.pyi +82 -82
- reflex/components/radix/themes/layout/box.pyi +17 -17
- reflex/components/radix/themes/layout/center.pyi +17 -17
- reflex/components/radix/themes/layout/container.pyi +17 -17
- reflex/components/radix/themes/layout/flex.py +1 -6
- reflex/components/radix/themes/layout/flex.pyi +17 -17
- reflex/components/radix/themes/layout/grid.py +1 -6
- reflex/components/radix/themes/layout/grid.pyi +17 -17
- reflex/components/radix/themes/layout/list.py +20 -15
- reflex/components/radix/themes/layout/list.pyi +175 -92
- reflex/components/radix/themes/layout/section.pyi +17 -17
- reflex/components/radix/themes/layout/spacer.pyi +17 -17
- reflex/components/radix/themes/layout/stack.py +6 -6
- reflex/components/radix/themes/layout/stack.pyi +91 -62
- reflex/components/radix/themes/typography/blockquote.py +2 -8
- reflex/components/radix/themes/typography/blockquote.pyi +17 -17
- reflex/components/radix/themes/typography/code.py +4 -10
- reflex/components/radix/themes/typography/code.pyi +19 -18
- reflex/components/radix/themes/typography/heading.py +4 -11
- reflex/components/radix/themes/typography/heading.pyi +19 -18
- reflex/components/radix/themes/typography/link.py +4 -10
- reflex/components/radix/themes/typography/link.pyi +19 -18
- reflex/components/radix/themes/typography/text.py +4 -11
- reflex/components/radix/themes/typography/text.pyi +115 -114
- reflex/components/react_player/audio.pyi +58 -33
- reflex/components/react_player/react_player.py +17 -17
- reflex/components/react_player/react_player.pyi +55 -33
- reflex/components/react_player/video.pyi +58 -33
- reflex/components/recharts/cartesian.py +45 -45
- reflex/components/recharts/cartesian.pyi +389 -304
- reflex/components/recharts/charts.py +22 -22
- reflex/components/recharts/charts.pyi +226 -179
- reflex/components/recharts/general.py +26 -27
- reflex/components/recharts/general.pyi +106 -99
- reflex/components/recharts/polar.py +33 -33
- reflex/components/recharts/polar.pyi +70 -64
- reflex/components/recharts/recharts.pyi +33 -33
- reflex/components/sonner/toast.py +9 -36
- reflex/components/sonner/toast.pyi +20 -24
- reflex/components/suneditor/editor.py +8 -8
- reflex/components/suneditor/editor.pyi +50 -25
- reflex/components/tags/iter_tag.py +1 -10
- reflex/components/tags/tag.py +1 -4
- reflex/config.py +198 -35
- reflex/constants/__init__.py +4 -16
- reflex/constants/base.py +7 -14
- reflex/constants/colors.py +0 -1
- reflex/constants/installer.py +12 -7
- reflex/constants/state.py +4 -0
- reflex/custom_components/custom_components.py +6 -6
- reflex/event.py +486 -241
- reflex/experimental/client_state.py +9 -9
- reflex/experimental/layout.py +2 -2
- reflex/experimental/layout.pyi +95 -87
- reflex/experimental/misc.py +1 -1
- reflex/istate/__init__.py +1 -0
- reflex/istate/proxy.py +33 -0
- reflex/istate/wrappers.py +27 -0
- reflex/model.py +7 -7
- reflex/page.py +2 -1
- reflex/reflex.py +142 -8
- reflex/state.py +127 -46
- reflex/testing.py +9 -7
- reflex/utils/console.py +0 -1
- reflex/utils/exceptions.py +31 -3
- reflex/utils/exec.py +33 -14
- reflex/utils/format.py +15 -12
- reflex/utils/net.py +1 -1
- reflex/utils/path_ops.py +2 -2
- reflex/utils/prerequisites.py +82 -46
- reflex/utils/pyi_generator.py +63 -20
- reflex/utils/registry.py +1 -1
- reflex/utils/serializers.py +75 -36
- reflex/utils/telemetry.py +3 -2
- reflex/utils/types.py +125 -10
- reflex/vars/base.py +131 -119
- reflex/vars/function.py +59 -12
- reflex/vars/number.py +3 -1
- reflex/vars/object.py +30 -24
- reflex/vars/sequence.py +7 -7
- {reflex-0.6.4a3.dist-info → reflex-0.6.5a1.dist-info}/METADATA +3 -3
- reflex-0.6.5a1.dist-info/RECORD +394 -0
- reflex-0.6.4a3.dist-info/RECORD +0 -391
- {reflex-0.6.4a3.dist-info → reflex-0.6.5a1.dist-info}/LICENSE +0 -0
- {reflex-0.6.4a3.dist-info → reflex-0.6.5a1.dist-info}/WHEEL +0 -0
- {reflex-0.6.4a3.dist-info → reflex-0.6.5a1.dist-info}/entry_points.txt +0 -0
reflex/utils/serializers.py
CHANGED
|
@@ -18,6 +18,7 @@ from typing import (
|
|
|
18
18
|
Set,
|
|
19
19
|
Tuple,
|
|
20
20
|
Type,
|
|
21
|
+
TypeVar,
|
|
21
22
|
Union,
|
|
22
23
|
get_type_hints,
|
|
23
24
|
overload,
|
|
@@ -32,17 +33,33 @@ from reflex.utils import types
|
|
|
32
33
|
SerializedType = Union[str, bool, int, float, list, dict, None]
|
|
33
34
|
|
|
34
35
|
|
|
35
|
-
Serializer = Callable[[
|
|
36
|
+
Serializer = Callable[[Any], SerializedType]
|
|
36
37
|
|
|
37
38
|
|
|
38
39
|
SERIALIZERS: dict[Type, Serializer] = {}
|
|
39
40
|
SERIALIZER_TYPES: dict[Type, Type] = {}
|
|
40
41
|
|
|
42
|
+
SERIALIZED_FUNCTION = TypeVar("SERIALIZED_FUNCTION", bound=Serializer)
|
|
41
43
|
|
|
44
|
+
|
|
45
|
+
@overload
|
|
46
|
+
def serializer(
|
|
47
|
+
fn: None = None,
|
|
48
|
+
to: Type[SerializedType] | None = None,
|
|
49
|
+
) -> Callable[[SERIALIZED_FUNCTION], SERIALIZED_FUNCTION]: ...
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@overload
|
|
42
53
|
def serializer(
|
|
43
|
-
fn:
|
|
44
|
-
to: Type | None = None,
|
|
45
|
-
) ->
|
|
54
|
+
fn: SERIALIZED_FUNCTION,
|
|
55
|
+
to: Type[SerializedType] | None = None,
|
|
56
|
+
) -> SERIALIZED_FUNCTION: ...
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def serializer(
|
|
60
|
+
fn: SERIALIZED_FUNCTION | None = None,
|
|
61
|
+
to: Any = None,
|
|
62
|
+
) -> SERIALIZED_FUNCTION | Callable[[SERIALIZED_FUNCTION], SERIALIZED_FUNCTION]:
|
|
46
63
|
"""Decorator to add a serializer for a given type.
|
|
47
64
|
|
|
48
65
|
Args:
|
|
@@ -51,43 +68,44 @@ def serializer(
|
|
|
51
68
|
|
|
52
69
|
Returns:
|
|
53
70
|
The decorated function.
|
|
54
|
-
|
|
55
|
-
Raises:
|
|
56
|
-
ValueError: If the function does not take a single argument.
|
|
57
71
|
"""
|
|
58
|
-
if fn is None:
|
|
59
|
-
# If the function is not provided, return a partial that acts as a decorator.
|
|
60
|
-
return functools.partial(serializer, to=to) # type: ignore
|
|
61
72
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
73
|
+
def wrapper(fn: SERIALIZED_FUNCTION) -> SERIALIZED_FUNCTION:
|
|
74
|
+
# Check the type hints to get the type of the argument.
|
|
75
|
+
type_hints = get_type_hints(fn)
|
|
76
|
+
args = [arg for arg in type_hints if arg != "return"]
|
|
77
|
+
|
|
78
|
+
# Make sure the function takes a single argument.
|
|
79
|
+
if len(args) != 1:
|
|
80
|
+
raise ValueError("Serializer must take a single argument.")
|
|
81
|
+
|
|
82
|
+
# Get the type of the argument.
|
|
83
|
+
type_ = type_hints[args[0]]
|
|
65
84
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
85
|
+
# Make sure the type is not already registered.
|
|
86
|
+
registered_fn = SERIALIZERS.get(type_)
|
|
87
|
+
if registered_fn is not None and registered_fn != fn:
|
|
88
|
+
raise ValueError(
|
|
89
|
+
f"Serializer for type {type_} is already registered as {registered_fn.__qualname__}."
|
|
90
|
+
)
|
|
69
91
|
|
|
70
|
-
|
|
71
|
-
type_ = type_hints[args[0]]
|
|
92
|
+
to_type = to or type_hints.get("return")
|
|
72
93
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
f"Serializer for type {type_} is already registered as {registered_fn.__qualname__}."
|
|
78
|
-
)
|
|
94
|
+
# Apply type transformation if requested
|
|
95
|
+
if to_type:
|
|
96
|
+
SERIALIZER_TYPES[type_] = to_type
|
|
97
|
+
get_serializer_type.cache_clear()
|
|
79
98
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
get_serializer_type.cache_clear()
|
|
99
|
+
# Register the serializer.
|
|
100
|
+
SERIALIZERS[type_] = fn
|
|
101
|
+
get_serializer.cache_clear()
|
|
84
102
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
get_serializer.cache_clear()
|
|
103
|
+
# Return the function.
|
|
104
|
+
return fn
|
|
88
105
|
|
|
89
|
-
|
|
90
|
-
|
|
106
|
+
if fn is not None:
|
|
107
|
+
return wrapper(fn)
|
|
108
|
+
return wrapper
|
|
91
109
|
|
|
92
110
|
|
|
93
111
|
@overload
|
|
@@ -189,16 +207,37 @@ def get_serializer_type(type_: Type) -> Optional[Type]:
|
|
|
189
207
|
return None
|
|
190
208
|
|
|
191
209
|
|
|
192
|
-
def has_serializer(type_: Type) -> bool:
|
|
210
|
+
def has_serializer(type_: Type, into_type: Type | None = None) -> bool:
|
|
193
211
|
"""Check if there is a serializer for the type.
|
|
194
212
|
|
|
195
213
|
Args:
|
|
196
214
|
type_: The type to check.
|
|
215
|
+
into_type: The type to serialize into.
|
|
197
216
|
|
|
198
217
|
Returns:
|
|
199
218
|
Whether there is a serializer for the type.
|
|
200
219
|
"""
|
|
201
|
-
|
|
220
|
+
serializer_for_type = get_serializer(type_)
|
|
221
|
+
return serializer_for_type is not None and (
|
|
222
|
+
into_type is None or get_serializer_type(type_) == into_type
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def can_serialize(type_: Type, into_type: Type | None = None) -> bool:
|
|
227
|
+
"""Check if there is a serializer for the type.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
type_: The type to check.
|
|
231
|
+
into_type: The type to serialize into.
|
|
232
|
+
|
|
233
|
+
Returns:
|
|
234
|
+
Whether there is a serializer for the type.
|
|
235
|
+
"""
|
|
236
|
+
return has_serializer(type_, into_type) or (
|
|
237
|
+
isinstance(type_, type)
|
|
238
|
+
and dataclasses.is_dataclass(type_)
|
|
239
|
+
and (into_type is None or into_type is dict)
|
|
240
|
+
)
|
|
202
241
|
|
|
203
242
|
|
|
204
243
|
@serializer(to=str)
|
|
@@ -214,7 +253,7 @@ def serialize_type(value: type) -> str:
|
|
|
214
253
|
return value.__name__
|
|
215
254
|
|
|
216
255
|
|
|
217
|
-
@serializer
|
|
256
|
+
@serializer(to=dict)
|
|
218
257
|
def serialize_base(value: Base) -> dict:
|
|
219
258
|
"""Serialize a Base instance.
|
|
220
259
|
|
reflex/utils/telemetry.py
CHANGED
|
@@ -8,6 +8,8 @@ import multiprocessing
|
|
|
8
8
|
import platform
|
|
9
9
|
import warnings
|
|
10
10
|
|
|
11
|
+
from reflex.config import environment
|
|
12
|
+
|
|
11
13
|
try:
|
|
12
14
|
from datetime import UTC, datetime
|
|
13
15
|
except ImportError:
|
|
@@ -20,7 +22,6 @@ import psutil
|
|
|
20
22
|
|
|
21
23
|
from reflex import constants
|
|
22
24
|
from reflex.utils import console
|
|
23
|
-
from reflex.utils.exec import should_skip_compile
|
|
24
25
|
from reflex.utils.prerequisites import ensure_reflex_installation_id, get_project_hash
|
|
25
26
|
|
|
26
27
|
POSTHOG_API_URL: str = "https://app.posthog.com/capture/"
|
|
@@ -94,7 +95,7 @@ def _raise_on_missing_project_hash() -> bool:
|
|
|
94
95
|
False when compilation should be skipped (i.e. no .web directory is required).
|
|
95
96
|
Otherwise return True.
|
|
96
97
|
"""
|
|
97
|
-
return not
|
|
98
|
+
return not environment.REFLEX_SKIP_COMPILE.get()
|
|
98
99
|
|
|
99
100
|
|
|
100
101
|
def _prepare_event(event: str, **kwargs) -> dict:
|
reflex/utils/types.py
CHANGED
|
@@ -26,9 +26,7 @@ from typing import (
|
|
|
26
26
|
get_args,
|
|
27
27
|
get_type_hints,
|
|
28
28
|
)
|
|
29
|
-
from typing import
|
|
30
|
-
get_origin as get_origin_og,
|
|
31
|
-
)
|
|
29
|
+
from typing import get_origin as get_origin_og
|
|
32
30
|
|
|
33
31
|
import sqlalchemy
|
|
34
32
|
|
|
@@ -42,12 +40,7 @@ except ModuleNotFoundError:
|
|
|
42
40
|
|
|
43
41
|
from sqlalchemy.ext.associationproxy import AssociationProxyInstance
|
|
44
42
|
from sqlalchemy.ext.hybrid import hybrid_property
|
|
45
|
-
from sqlalchemy.orm import
|
|
46
|
-
DeclarativeBase,
|
|
47
|
-
Mapped,
|
|
48
|
-
QueryableAttribute,
|
|
49
|
-
Relationship,
|
|
50
|
-
)
|
|
43
|
+
from sqlalchemy.orm import DeclarativeBase, Mapped, QueryableAttribute, Relationship
|
|
51
44
|
|
|
52
45
|
from reflex import constants
|
|
53
46
|
from reflex.base import Base
|
|
@@ -510,16 +503,72 @@ def _issubclass(cls: GenericType, cls_check: GenericType, instance: Any = None)
|
|
|
510
503
|
raise TypeError(f"Invalid type for issubclass: {cls_base}") from te
|
|
511
504
|
|
|
512
505
|
|
|
513
|
-
def _isinstance(obj: Any, cls: GenericType) -> bool:
|
|
506
|
+
def _isinstance(obj: Any, cls: GenericType, nested: bool = False) -> bool:
|
|
514
507
|
"""Check if an object is an instance of a class.
|
|
515
508
|
|
|
516
509
|
Args:
|
|
517
510
|
obj: The object to check.
|
|
518
511
|
cls: The class to check against.
|
|
512
|
+
nested: Whether the check is nested.
|
|
519
513
|
|
|
520
514
|
Returns:
|
|
521
515
|
Whether the object is an instance of the class.
|
|
522
516
|
"""
|
|
517
|
+
if cls is Any:
|
|
518
|
+
return True
|
|
519
|
+
|
|
520
|
+
if cls is None or cls is type(None):
|
|
521
|
+
return obj is None
|
|
522
|
+
|
|
523
|
+
if is_literal(cls):
|
|
524
|
+
return obj in get_args(cls)
|
|
525
|
+
|
|
526
|
+
if is_union(cls):
|
|
527
|
+
return any(_isinstance(obj, arg) for arg in get_args(cls))
|
|
528
|
+
|
|
529
|
+
origin = get_origin(cls)
|
|
530
|
+
|
|
531
|
+
if origin is None:
|
|
532
|
+
# cls is a simple class
|
|
533
|
+
return isinstance(obj, cls)
|
|
534
|
+
|
|
535
|
+
args = get_args(cls)
|
|
536
|
+
|
|
537
|
+
if not args:
|
|
538
|
+
# cls is a simple generic class
|
|
539
|
+
return isinstance(obj, origin)
|
|
540
|
+
|
|
541
|
+
if nested and args:
|
|
542
|
+
if origin is list:
|
|
543
|
+
return isinstance(obj, list) and all(
|
|
544
|
+
_isinstance(item, args[0]) for item in obj
|
|
545
|
+
)
|
|
546
|
+
if origin is tuple:
|
|
547
|
+
if args[-1] is Ellipsis:
|
|
548
|
+
return isinstance(obj, tuple) and all(
|
|
549
|
+
_isinstance(item, args[0]) for item in obj
|
|
550
|
+
)
|
|
551
|
+
return (
|
|
552
|
+
isinstance(obj, tuple)
|
|
553
|
+
and len(obj) == len(args)
|
|
554
|
+
and all(_isinstance(item, arg) for item, arg in zip(obj, args))
|
|
555
|
+
)
|
|
556
|
+
if origin is dict:
|
|
557
|
+
return isinstance(obj, dict) and all(
|
|
558
|
+
_isinstance(key, args[0]) and _isinstance(value, args[1])
|
|
559
|
+
for key, value in obj.items()
|
|
560
|
+
)
|
|
561
|
+
if origin is set:
|
|
562
|
+
return isinstance(obj, set) and all(
|
|
563
|
+
_isinstance(item, args[0]) for item in obj
|
|
564
|
+
)
|
|
565
|
+
|
|
566
|
+
if args:
|
|
567
|
+
from reflex.vars import Field
|
|
568
|
+
|
|
569
|
+
if origin is Field:
|
|
570
|
+
return _isinstance(obj, args[0])
|
|
571
|
+
|
|
523
572
|
return isinstance(obj, get_base_class(cls))
|
|
524
573
|
|
|
525
574
|
|
|
@@ -724,3 +773,69 @@ def validate_parameter_literals(func):
|
|
|
724
773
|
# Store this here for performance.
|
|
725
774
|
StateBases = get_base_class(StateVar)
|
|
726
775
|
StateIterBases = get_base_class(StateIterVar)
|
|
776
|
+
|
|
777
|
+
|
|
778
|
+
def typehint_issubclass(possible_subclass: Any, possible_superclass: Any) -> bool:
|
|
779
|
+
"""Check if a type hint is a subclass of another type hint.
|
|
780
|
+
|
|
781
|
+
Args:
|
|
782
|
+
possible_subclass: The type hint to check.
|
|
783
|
+
possible_superclass: The type hint to check against.
|
|
784
|
+
|
|
785
|
+
Returns:
|
|
786
|
+
Whether the type hint is a subclass of the other type hint.
|
|
787
|
+
"""
|
|
788
|
+
if possible_superclass is Any:
|
|
789
|
+
return True
|
|
790
|
+
if possible_subclass is Any:
|
|
791
|
+
return False
|
|
792
|
+
|
|
793
|
+
provided_type_origin = get_origin(possible_subclass)
|
|
794
|
+
accepted_type_origin = get_origin(possible_superclass)
|
|
795
|
+
|
|
796
|
+
if provided_type_origin is None and accepted_type_origin is None:
|
|
797
|
+
# In this case, we are dealing with a non-generic type, so we can use issubclass
|
|
798
|
+
return issubclass(possible_subclass, possible_superclass)
|
|
799
|
+
|
|
800
|
+
# Remove this check when Python 3.10 is the minimum supported version
|
|
801
|
+
if hasattr(types, "UnionType"):
|
|
802
|
+
provided_type_origin = (
|
|
803
|
+
Union if provided_type_origin is types.UnionType else provided_type_origin
|
|
804
|
+
)
|
|
805
|
+
accepted_type_origin = (
|
|
806
|
+
Union if accepted_type_origin is types.UnionType else accepted_type_origin
|
|
807
|
+
)
|
|
808
|
+
|
|
809
|
+
# Get type arguments (e.g., [float, int] for Dict[float, int])
|
|
810
|
+
provided_args = get_args(possible_subclass)
|
|
811
|
+
accepted_args = get_args(possible_superclass)
|
|
812
|
+
|
|
813
|
+
if accepted_type_origin is Union:
|
|
814
|
+
if provided_type_origin is not Union:
|
|
815
|
+
return any(
|
|
816
|
+
typehint_issubclass(possible_subclass, accepted_arg)
|
|
817
|
+
for accepted_arg in accepted_args
|
|
818
|
+
)
|
|
819
|
+
return all(
|
|
820
|
+
any(
|
|
821
|
+
typehint_issubclass(provided_arg, accepted_arg)
|
|
822
|
+
for accepted_arg in accepted_args
|
|
823
|
+
)
|
|
824
|
+
for provided_arg in provided_args
|
|
825
|
+
)
|
|
826
|
+
|
|
827
|
+
# Check if the origin of both types is the same (e.g., list for List[int])
|
|
828
|
+
# This probably should be issubclass instead of ==
|
|
829
|
+
if (provided_type_origin or possible_subclass) != (
|
|
830
|
+
accepted_type_origin or possible_superclass
|
|
831
|
+
):
|
|
832
|
+
return False
|
|
833
|
+
|
|
834
|
+
# Ensure all specific types are compatible with accepted types
|
|
835
|
+
# Note this is not necessarily correct, as it doesn't check against contravariance and covariance
|
|
836
|
+
# It also ignores when the length of the arguments is different
|
|
837
|
+
return all(
|
|
838
|
+
typehint_issubclass(provided_arg, accepted_arg)
|
|
839
|
+
for provided_arg, accepted_arg in zip(provided_args, accepted_args)
|
|
840
|
+
if accepted_arg is not Any
|
|
841
|
+
)
|