reflex 0.7.1a3__py3-none-any.whl → 0.7.2__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/web/utils/context.js.jinja2 +8 -8
- reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js +3 -3
- reflex/.templates/web/utils/state.js +18 -18
- reflex/admin.py +1 -2
- reflex/app.py +53 -50
- reflex/app_mixins/lifespan.py +2 -2
- reflex/app_mixins/middleware.py +1 -2
- reflex/assets.py +1 -2
- reflex/base.py +2 -2
- reflex/compiler/compiler.py +51 -16
- reflex/compiler/utils.py +4 -13
- reflex/components/base/app_wrap.pyi +7 -7
- reflex/components/base/bare.py +3 -3
- reflex/components/base/body.pyi +7 -7
- reflex/components/base/document.py +1 -3
- reflex/components/base/document.pyi +32 -32
- reflex/components/base/error_boundary.py +2 -4
- reflex/components/base/error_boundary.pyi +11 -13
- reflex/components/base/fragment.pyi +7 -7
- reflex/components/base/head.pyi +13 -13
- reflex/components/base/link.pyi +22 -22
- reflex/components/base/meta.py +5 -7
- reflex/components/base/meta.pyi +40 -40
- reflex/components/base/script.pyi +11 -14
- reflex/components/base/strict_mode.pyi +7 -7
- reflex/components/component.py +188 -113
- reflex/components/core/auto_scroll.py +8 -1
- reflex/components/core/auto_scroll.pyi +183 -210
- reflex/components/core/banner.py +2 -4
- reflex/components/core/banner.pyi +390 -444
- reflex/components/core/breakpoints.py +5 -5
- reflex/components/core/client_side_routing.pyi +14 -14
- reflex/components/core/clipboard.py +4 -4
- reflex/components/core/clipboard.pyi +12 -14
- reflex/components/core/cond.py +17 -25
- reflex/components/core/debounce.py +3 -3
- reflex/components/core/debounce.pyi +14 -14
- reflex/components/core/foreach.py +7 -2
- reflex/components/core/html.py +1 -3
- reflex/components/core/html.pyi +184 -213
- reflex/components/core/match.py +15 -19
- reflex/components/core/sticky.pyi +930 -1078
- reflex/components/core/upload.py +4 -4
- reflex/components/core/upload.pyi +62 -62
- reflex/components/datadisplay/code.py +6 -6
- reflex/components/datadisplay/code.pyi +1159 -1165
- reflex/components/datadisplay/dataeditor.py +49 -49
- reflex/components/datadisplay/dataeditor.pyi +95 -123
- reflex/components/datadisplay/logo.py +1 -3
- reflex/components/datadisplay/shiki_code_block.py +8 -10
- reflex/components/datadisplay/shiki_code_block.pyi +1678 -1720
- reflex/components/el/element.pyi +7 -7
- reflex/components/el/elements/base.pyi +183 -210
- reflex/components/el/elements/forms.py +24 -24
- reflex/components/el/elements/forms.pyi +2572 -2934
- reflex/components/el/elements/inline.py +4 -4
- reflex/components/el/elements/inline.pyi +5191 -5953
- reflex/components/el/elements/media.py +47 -47
- reflex/components/el/elements/media.pyi +4802 -5500
- reflex/components/el/elements/metadata.py +1 -3
- reflex/components/el/elements/metadata.pyi +782 -896
- reflex/components/el/elements/other.pyi +1278 -1467
- reflex/components/el/elements/scripts.pyi +580 -667
- reflex/components/el/elements/sectioning.pyi +2761 -3166
- reflex/components/el/elements/tables.pyi +1840 -2119
- reflex/components/el/elements/typography.pyi +2772 -3179
- reflex/components/gridjs/datatable.py +7 -7
- reflex/components/gridjs/datatable.pyi +19 -19
- reflex/components/lucide/icon.pyi +21 -21
- reflex/components/markdown/markdown.py +2 -2
- reflex/components/markdown/markdown.pyi +9 -9
- reflex/components/moment/moment.py +11 -12
- reflex/components/moment/moment.pyi +44 -47
- reflex/components/next/base.pyi +7 -7
- reflex/components/next/image.py +3 -3
- reflex/components/next/image.pyi +19 -21
- reflex/components/next/link.pyi +9 -9
- reflex/components/next/video.py +1 -3
- reflex/components/next/video.pyi +9 -9
- reflex/components/plotly/plotly.py +22 -45
- reflex/components/plotly/plotly.pyi +164 -164
- reflex/components/radix/primitives/accordion.py +14 -14
- reflex/components/radix/primitives/accordion.pyi +439 -487
- reflex/components/radix/primitives/base.py +1 -3
- reflex/components/radix/primitives/base.pyi +15 -15
- reflex/components/radix/primitives/drawer.py +3 -3
- reflex/components/radix/primitives/drawer.pyi +110 -116
- reflex/components/radix/primitives/form.py +1 -1
- reflex/components/radix/primitives/form.pyi +668 -752
- reflex/components/radix/primitives/progress.py +6 -6
- reflex/components/radix/primitives/progress.pyi +225 -243
- reflex/components/radix/primitives/slider.py +6 -6
- reflex/components/radix/primitives/slider.pyi +52 -55
- reflex/components/radix/themes/base.py +3 -6
- reflex/components/radix/themes/base.pyi +197 -303
- reflex/components/radix/themes/color_mode.py +5 -5
- reflex/components/radix/themes/color_mode.pyi +366 -436
- reflex/components/radix/themes/components/alert_dialog.pyi +229 -262
- reflex/components/radix/themes/components/aspect_ratio.py +1 -3
- reflex/components/radix/themes/components/aspect_ratio.pyi +8 -8
- reflex/components/radix/themes/components/avatar.pyi +79 -94
- reflex/components/radix/themes/components/badge.pyi +252 -295
- reflex/components/radix/themes/components/button.pyi +269 -314
- reflex/components/radix/themes/components/callout.py +2 -2
- reflex/components/radix/themes/components/callout.pyi +1116 -1290
- reflex/components/radix/themes/components/card.pyi +194 -229
- reflex/components/radix/themes/components/checkbox.pyi +243 -278
- reflex/components/radix/themes/components/checkbox_cards.py +3 -7
- reflex/components/radix/themes/components/checkbox_cards.pyi +101 -135
- reflex/components/radix/themes/components/checkbox_group.py +2 -2
- reflex/components/radix/themes/components/checkbox_group.pyi +83 -96
- reflex/components/radix/themes/components/context_menu.py +18 -15
- reflex/components/radix/themes/components/context_menu.pyi +408 -458
- reflex/components/radix/themes/components/data_list.pyi +122 -147
- reflex/components/radix/themes/components/dialog.pyi +231 -264
- reflex/components/radix/themes/components/dropdown_menu.py +16 -13
- reflex/components/radix/themes/components/dropdown_menu.pyi +223 -246
- reflex/components/radix/themes/components/hover_card.py +2 -2
- reflex/components/radix/themes/components/hover_card.pyi +237 -282
- reflex/components/radix/themes/components/icon_button.pyi +269 -314
- reflex/components/radix/themes/components/inset.py +8 -8
- reflex/components/radix/themes/components/inset.pyi +232 -292
- reflex/components/radix/themes/components/popover.py +2 -2
- reflex/components/radix/themes/components/popover.pyi +229 -271
- reflex/components/radix/themes/components/progress.pyi +80 -96
- reflex/components/radix/themes/components/radio.pyi +73 -86
- reflex/components/radix/themes/components/radio_cards.py +4 -8
- reflex/components/radix/themes/components/radio_cards.pyi +117 -154
- reflex/components/radix/themes/components/radio_group.py +3 -3
- reflex/components/radix/themes/components/radio_group.pyi +250 -291
- reflex/components/radix/themes/components/scroll_area.pyi +14 -20
- reflex/components/radix/themes/components/segmented_control.py +6 -6
- reflex/components/radix/themes/components/segmented_control.pyi +89 -108
- reflex/components/radix/themes/components/select.py +7 -7
- reflex/components/radix/themes/components/select.pyi +376 -444
- reflex/components/radix/themes/components/separator.pyi +79 -93
- reflex/components/radix/themes/components/skeleton.pyi +32 -26
- reflex/components/radix/themes/components/slider.py +8 -8
- reflex/components/radix/themes/components/slider.pyi +99 -122
- reflex/components/radix/themes/components/spinner.pyi +12 -19
- reflex/components/radix/themes/components/switch.pyi +84 -99
- reflex/components/radix/themes/components/table.py +9 -9
- reflex/components/radix/themes/components/table.pyi +1440 -1794
- reflex/components/radix/themes/components/tabs.py +4 -4
- reflex/components/radix/themes/components/tabs.pyi +120 -132
- reflex/components/radix/themes/components/text_area.pyi +281 -331
- reflex/components/radix/themes/components/text_field.py +2 -2
- reflex/components/radix/themes/components/text_field.pyi +639 -734
- reflex/components/radix/themes/components/tooltip.py +6 -6
- reflex/components/radix/themes/components/tooltip.pyi +34 -43
- reflex/components/radix/themes/layout/base.pyi +85 -182
- reflex/components/radix/themes/layout/box.pyi +183 -210
- reflex/components/radix/themes/layout/center.pyi +225 -286
- reflex/components/radix/themes/layout/container.pyi +191 -224
- reflex/components/radix/themes/layout/flex.py +2 -2
- reflex/components/radix/themes/layout/flex.pyi +225 -286
- reflex/components/radix/themes/layout/grid.py +2 -2
- reflex/components/radix/themes/layout/grid.pyi +245 -315
- reflex/components/radix/themes/layout/list.py +2 -2
- reflex/components/radix/themes/layout/list.pyi +712 -815
- reflex/components/radix/themes/layout/section.pyi +187 -221
- reflex/components/radix/themes/layout/spacer.pyi +225 -286
- reflex/components/radix/themes/layout/stack.pyi +625 -768
- reflex/components/radix/themes/typography/blockquote.pyi +257 -299
- reflex/components/radix/themes/typography/code.pyi +259 -304
- reflex/components/radix/themes/typography/heading.pyi +272 -324
- reflex/components/radix/themes/typography/link.pyi +302 -358
- reflex/components/radix/themes/typography/text.pyi +1669 -1945
- reflex/components/react_player/audio.pyi +20 -22
- reflex/components/react_player/react_player.pyi +19 -19
- reflex/components/react_player/video.pyi +20 -22
- reflex/components/recharts/cartesian.py +100 -97
- reflex/components/recharts/cartesian.pyi +891 -1007
- reflex/components/recharts/charts.py +42 -42
- reflex/components/recharts/charts.pyi +212 -249
- reflex/components/recharts/general.py +22 -21
- reflex/components/recharts/general.pyi +198 -223
- reflex/components/recharts/polar.py +42 -45
- reflex/components/recharts/polar.pyi +254 -288
- reflex/components/recharts/recharts.pyi +13 -13
- reflex/components/sonner/toast.py +20 -20
- reflex/components/sonner/toast.pyi +58 -61
- reflex/components/suneditor/editor.py +9 -9
- reflex/components/suneditor/editor.pyi +78 -83
- reflex/components/tags/cond_tag.py +2 -2
- reflex/components/tags/iter_tag.py +10 -14
- reflex/components/tags/match_tag.py +2 -2
- reflex/components/tags/tag.py +10 -10
- reflex/config.py +36 -35
- reflex/constants/__init__.py +56 -53
- reflex/custom_components/custom_components.py +6 -7
- reflex/event.py +38 -42
- reflex/experimental/client_state.py +2 -4
- reflex/experimental/layout.py +2 -2
- reflex/experimental/layout.pyi +579 -663
- reflex/istate/data.py +4 -5
- reflex/middleware/hydrate_middleware.py +2 -2
- reflex/middleware/middleware.py +2 -2
- reflex/model.py +3 -5
- reflex/page.py +2 -2
- reflex/reflex.py +9 -10
- reflex/state.py +77 -49
- reflex/style.py +11 -5
- reflex/testing.py +21 -24
- reflex/utils/console.py +1 -1
- reflex/utils/decorator.py +26 -1
- reflex/utils/exec.py +6 -11
- reflex/utils/export.py +2 -3
- reflex/utils/format.py +4 -4
- reflex/utils/imports.py +12 -12
- reflex/utils/prerequisites.py +35 -84
- reflex/utils/processes.py +5 -5
- reflex/utils/pyi_generator.py +33 -22
- reflex/utils/serializers.py +60 -15
- reflex/utils/types.py +237 -56
- reflex/vars/base.py +122 -72
- reflex/vars/datetime.py +2 -2
- reflex/vars/function.py +52 -55
- reflex/vars/number.py +59 -5
- reflex/vars/object.py +57 -26
- reflex/vars/sequence.py +983 -958
- {reflex-0.7.1a3.dist-info → reflex-0.7.2.dist-info}/METADATA +3 -6
- reflex-0.7.2.dist-info/RECORD +405 -0
- {reflex-0.7.1a3.dist-info → reflex-0.7.2.dist-info}/WHEEL +1 -1
- reflex-0.7.1a3.dist-info/RECORD +0 -405
- {reflex-0.7.1a3.dist-info → reflex-0.7.2.dist-info}/LICENSE +0 -0
- {reflex-0.7.1a3.dist-info → reflex-0.7.2.dist-info}/entry_points.txt +0 -0
reflex/utils/types.py
CHANGED
|
@@ -14,11 +14,13 @@ from typing import (
|
|
|
14
14
|
Callable,
|
|
15
15
|
ClassVar,
|
|
16
16
|
Dict,
|
|
17
|
+
ForwardRef,
|
|
17
18
|
FrozenSet,
|
|
18
19
|
Iterable,
|
|
19
20
|
List,
|
|
20
21
|
Literal,
|
|
21
22
|
Mapping,
|
|
23
|
+
NoReturn,
|
|
22
24
|
Optional,
|
|
23
25
|
Sequence,
|
|
24
26
|
Tuple,
|
|
@@ -27,9 +29,9 @@ from typing import (
|
|
|
27
29
|
_GenericAlias, # pyright: ignore [reportAttributeAccessIssue]
|
|
28
30
|
_SpecialGenericAlias, # pyright: ignore [reportAttributeAccessIssue]
|
|
29
31
|
get_args,
|
|
30
|
-
get_type_hints,
|
|
31
32
|
)
|
|
32
33
|
from typing import get_origin as get_origin_og
|
|
34
|
+
from typing import get_type_hints as get_type_hints_og
|
|
33
35
|
|
|
34
36
|
import sqlalchemy
|
|
35
37
|
from pydantic.v1.fields import ModelField
|
|
@@ -49,18 +51,18 @@ from reflex.utils import console
|
|
|
49
51
|
# Potential GenericAlias types for isinstance checks.
|
|
50
52
|
GenericAliasTypes = (_GenericAlias, GenericAlias, _SpecialGenericAlias)
|
|
51
53
|
|
|
52
|
-
# Potential Union types for isinstance checks
|
|
53
|
-
UnionTypes = (Union, types.UnionType)
|
|
54
|
+
# Potential Union types for isinstance checks.
|
|
55
|
+
UnionTypes = (Union, types.UnionType)
|
|
54
56
|
|
|
55
57
|
# Union of generic types.
|
|
56
|
-
GenericType =
|
|
58
|
+
GenericType = Type | _GenericAlias
|
|
57
59
|
|
|
58
60
|
# Valid state var types.
|
|
59
61
|
JSONType = {str, int, float, bool}
|
|
60
62
|
PrimitiveType = Union[int, float, bool, str, list, dict, set, tuple]
|
|
61
63
|
PrimitiveTypes = (int, float, bool, str, list, dict, set, tuple)
|
|
62
|
-
StateVar =
|
|
63
|
-
StateIterVar =
|
|
64
|
+
StateVar = PrimitiveType | Base | None
|
|
65
|
+
StateIterVar = list | set | tuple
|
|
64
66
|
|
|
65
67
|
if TYPE_CHECKING:
|
|
66
68
|
from reflex.vars.base import Var
|
|
@@ -76,7 +78,7 @@ if TYPE_CHECKING:
|
|
|
76
78
|
| Callable[[Var, Var, Var, Var, Var, Var, Var], Sequence[Var]]
|
|
77
79
|
)
|
|
78
80
|
else:
|
|
79
|
-
ArgsSpec = Callable[...,
|
|
81
|
+
ArgsSpec = Callable[..., list[Any]]
|
|
80
82
|
|
|
81
83
|
|
|
82
84
|
PrimitiveToAnnotation = {
|
|
@@ -141,15 +143,20 @@ def is_generic_alias(cls: GenericType) -> bool:
|
|
|
141
143
|
return isinstance(cls, GenericAliasTypes) # pyright: ignore [reportArgumentType]
|
|
142
144
|
|
|
143
145
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
+
@lru_cache()
|
|
147
|
+
def get_type_hints(obj: Any) -> Dict[str, Any]:
|
|
148
|
+
"""Get the type hints of a class.
|
|
146
149
|
|
|
147
150
|
Args:
|
|
148
|
-
|
|
151
|
+
obj: The class to get the type hints of.
|
|
149
152
|
|
|
150
153
|
Returns:
|
|
151
|
-
The
|
|
154
|
+
The type hints of the class.
|
|
152
155
|
"""
|
|
156
|
+
return get_type_hints_og(obj)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _unionize(args: list[GenericType]) -> Type:
|
|
153
160
|
if not args:
|
|
154
161
|
return Any # pyright: ignore [reportReturnType]
|
|
155
162
|
if len(args) == 1:
|
|
@@ -161,6 +168,18 @@ def unionize(*args: GenericType) -> Type:
|
|
|
161
168
|
return Union[unionize(*first_half), unionize(*second_half)] # pyright: ignore [reportReturnType]
|
|
162
169
|
|
|
163
170
|
|
|
171
|
+
def unionize(*args: GenericType) -> Type:
|
|
172
|
+
"""Unionize the types.
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
args: The types to unionize.
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
The unionized types.
|
|
179
|
+
"""
|
|
180
|
+
return _unionize([arg for arg in args if arg is not NoReturn])
|
|
181
|
+
|
|
182
|
+
|
|
164
183
|
def is_none(cls: GenericType) -> bool:
|
|
165
184
|
"""Check if a class is None.
|
|
166
185
|
|
|
@@ -232,6 +251,33 @@ def is_optional(cls: GenericType) -> bool:
|
|
|
232
251
|
return is_union(cls) and type(None) in get_args(cls)
|
|
233
252
|
|
|
234
253
|
|
|
254
|
+
def true_type_for_pydantic_field(f: ModelField):
|
|
255
|
+
"""Get the type for a pydantic field.
|
|
256
|
+
|
|
257
|
+
Args:
|
|
258
|
+
f: The field to get the type for.
|
|
259
|
+
|
|
260
|
+
Returns:
|
|
261
|
+
The type for the field.
|
|
262
|
+
"""
|
|
263
|
+
if not isinstance(f.annotation, (str, ForwardRef)):
|
|
264
|
+
return f.annotation
|
|
265
|
+
|
|
266
|
+
type_ = f.outer_type_
|
|
267
|
+
|
|
268
|
+
if (
|
|
269
|
+
f.field_info.default is None
|
|
270
|
+
or (isinstance(f.annotation, str) and f.annotation.startswith("Optional"))
|
|
271
|
+
or (
|
|
272
|
+
isinstance(f.annotation, ForwardRef)
|
|
273
|
+
and f.annotation.__forward_arg__.startswith("Optional")
|
|
274
|
+
)
|
|
275
|
+
) and not is_optional(type_):
|
|
276
|
+
return Optional[type_]
|
|
277
|
+
|
|
278
|
+
return type_
|
|
279
|
+
|
|
280
|
+
|
|
235
281
|
def value_inside_optional(cls: GenericType) -> GenericType:
|
|
236
282
|
"""Get the value inside an Optional type or the original type.
|
|
237
283
|
|
|
@@ -242,10 +288,33 @@ def value_inside_optional(cls: GenericType) -> GenericType:
|
|
|
242
288
|
The value inside the Optional type or the original type.
|
|
243
289
|
"""
|
|
244
290
|
if is_union(cls) and len(args := get_args(cls)) >= 2 and type(None) in args:
|
|
291
|
+
if len(args) == 2:
|
|
292
|
+
return args[0] if args[1] is type(None) else args[1]
|
|
245
293
|
return unionize(*[arg for arg in args if arg is not type(None)])
|
|
246
294
|
return cls
|
|
247
295
|
|
|
248
296
|
|
|
297
|
+
def get_field_type(cls: GenericType, field_name: str) -> GenericType | None:
|
|
298
|
+
"""Get the type of a field in a class.
|
|
299
|
+
|
|
300
|
+
Args:
|
|
301
|
+
cls: The class to check.
|
|
302
|
+
field_name: The name of the field to check.
|
|
303
|
+
|
|
304
|
+
Returns:
|
|
305
|
+
The type of the field, if it exists, else None.
|
|
306
|
+
"""
|
|
307
|
+
if (
|
|
308
|
+
hasattr(cls, "__fields__")
|
|
309
|
+
and field_name in cls.__fields__
|
|
310
|
+
and hasattr(cls.__fields__[field_name], "annotation")
|
|
311
|
+
and not isinstance(cls.__fields__[field_name].annotation, (str, ForwardRef))
|
|
312
|
+
):
|
|
313
|
+
return cls.__fields__[field_name].annotation
|
|
314
|
+
type_hints = get_type_hints(cls)
|
|
315
|
+
return type_hints.get(field_name, None)
|
|
316
|
+
|
|
317
|
+
|
|
249
318
|
def get_property_hint(attr: Any | None) -> GenericType | None:
|
|
250
319
|
"""Check if an attribute is a property and return its type hint.
|
|
251
320
|
|
|
@@ -283,24 +352,9 @@ def get_attribute_access_type(cls: GenericType, name: str) -> GenericType | None
|
|
|
283
352
|
if hint := get_property_hint(attr):
|
|
284
353
|
return hint
|
|
285
354
|
|
|
286
|
-
if (
|
|
287
|
-
hasattr(cls, "__fields__")
|
|
288
|
-
and name in cls.__fields__
|
|
289
|
-
and hasattr(cls.__fields__[name], "outer_type_")
|
|
290
|
-
):
|
|
355
|
+
if hasattr(cls, "__fields__") and name in cls.__fields__:
|
|
291
356
|
# pydantic models
|
|
292
|
-
|
|
293
|
-
type_ = field.outer_type_
|
|
294
|
-
if isinstance(type_, ModelField):
|
|
295
|
-
type_ = type_.type_
|
|
296
|
-
if (
|
|
297
|
-
not field.required
|
|
298
|
-
and field.default is None
|
|
299
|
-
and field.default_factory is None
|
|
300
|
-
):
|
|
301
|
-
# Ensure frontend uses null coalescing when accessing.
|
|
302
|
-
type_ = Optional[type_]
|
|
303
|
-
return type_
|
|
357
|
+
return get_field_type(cls, name)
|
|
304
358
|
elif isinstance(cls, type) and issubclass(cls, DeclarativeBase):
|
|
305
359
|
insp = sqlalchemy.inspect(cls)
|
|
306
360
|
if name in insp.columns:
|
|
@@ -322,7 +376,7 @@ def get_attribute_access_type(cls: GenericType, name: str) -> GenericType | None
|
|
|
322
376
|
type_ = PrimitiveToAnnotation[type_]
|
|
323
377
|
type_ = type_[item_type] # pyright: ignore [reportIndexIssue]
|
|
324
378
|
if column.nullable:
|
|
325
|
-
type_ =
|
|
379
|
+
type_ = type_ | None
|
|
326
380
|
return type_
|
|
327
381
|
if name in insp.all_orm_descriptors:
|
|
328
382
|
descriptor = insp.all_orm_descriptors[name]
|
|
@@ -333,10 +387,10 @@ def get_attribute_access_type(cls: GenericType, name: str) -> GenericType | None
|
|
|
333
387
|
if isinstance(prop, Relationship):
|
|
334
388
|
type_ = prop.mapper.class_
|
|
335
389
|
# TODO: check for nullable?
|
|
336
|
-
type_ =
|
|
390
|
+
type_ = list[type_] if prop.uselist else type_ | None
|
|
337
391
|
return type_
|
|
338
392
|
if isinstance(attr, AssociationProxyInstance):
|
|
339
|
-
return
|
|
393
|
+
return list[
|
|
340
394
|
get_attribute_access_type(
|
|
341
395
|
attr.target_class,
|
|
342
396
|
attr.remote_attr.key, # type: ignore[attr-defined]
|
|
@@ -510,13 +564,22 @@ def does_obj_satisfy_typed_dict(obj: Any, cls: GenericType) -> bool:
|
|
|
510
564
|
return required_keys.issubset(required_keys)
|
|
511
565
|
|
|
512
566
|
|
|
513
|
-
def _isinstance(
|
|
567
|
+
def _isinstance(
|
|
568
|
+
obj: Any,
|
|
569
|
+
cls: GenericType,
|
|
570
|
+
*,
|
|
571
|
+
nested: int = 0,
|
|
572
|
+
treat_var_as_type: bool = True,
|
|
573
|
+
treat_mutable_obj_as_immutable: bool = False,
|
|
574
|
+
) -> bool:
|
|
514
575
|
"""Check if an object is an instance of a class.
|
|
515
576
|
|
|
516
577
|
Args:
|
|
517
578
|
obj: The object to check.
|
|
518
579
|
cls: The class to check against.
|
|
519
580
|
nested: How many levels deep to check.
|
|
581
|
+
treat_var_as_type: Whether to treat Var as the type it represents, i.e. _var_type.
|
|
582
|
+
treat_mutable_obj_as_immutable: Whether to treat mutable objects as immutable. Useful if a component declares a mutable object as a prop, but the value is not expected to change.
|
|
520
583
|
|
|
521
584
|
Returns:
|
|
522
585
|
Whether the object is an instance of the class.
|
|
@@ -529,15 +592,26 @@ def _isinstance(obj: Any, cls: GenericType, nested: int = 0) -> bool:
|
|
|
529
592
|
if cls is Var:
|
|
530
593
|
return isinstance(obj, Var)
|
|
531
594
|
if isinstance(obj, LiteralVar):
|
|
532
|
-
return _isinstance(
|
|
595
|
+
return treat_var_as_type and _isinstance(
|
|
596
|
+
obj._var_value, cls, nested=nested, treat_var_as_type=True
|
|
597
|
+
)
|
|
533
598
|
if isinstance(obj, Var):
|
|
534
|
-
return
|
|
599
|
+
return treat_var_as_type and typehint_issubclass(
|
|
600
|
+
obj._var_type,
|
|
601
|
+
cls,
|
|
602
|
+
treat_mutable_superclasss_as_immutable=treat_mutable_obj_as_immutable,
|
|
603
|
+
treat_literals_as_union_of_types=True,
|
|
604
|
+
treat_any_as_subtype_of_everything=True,
|
|
605
|
+
)
|
|
535
606
|
|
|
536
607
|
if cls is None or cls is type(None):
|
|
537
608
|
return obj is None
|
|
538
609
|
|
|
539
610
|
if cls and is_union(cls):
|
|
540
|
-
return any(
|
|
611
|
+
return any(
|
|
612
|
+
_isinstance(obj, arg, nested=nested, treat_var_as_type=treat_var_as_type)
|
|
613
|
+
for arg in get_args(cls)
|
|
614
|
+
)
|
|
541
615
|
|
|
542
616
|
if is_literal(cls):
|
|
543
617
|
return obj in get_args(cls)
|
|
@@ -561,43 +635,87 @@ def _isinstance(obj: Any, cls: GenericType, nested: int = 0) -> bool:
|
|
|
561
635
|
args = get_args(cls)
|
|
562
636
|
|
|
563
637
|
if not args:
|
|
638
|
+
if treat_mutable_obj_as_immutable:
|
|
639
|
+
if origin is dict:
|
|
640
|
+
origin = Mapping
|
|
641
|
+
elif origin is list or origin is set:
|
|
642
|
+
origin = Sequence
|
|
564
643
|
# cls is a simple generic class
|
|
565
644
|
return isinstance(obj, origin)
|
|
566
645
|
|
|
567
646
|
if nested > 0 and args:
|
|
568
647
|
if origin is list:
|
|
569
|
-
|
|
570
|
-
|
|
648
|
+
expected_class = Sequence if treat_mutable_obj_as_immutable else list
|
|
649
|
+
return isinstance(obj, expected_class) and all(
|
|
650
|
+
_isinstance(
|
|
651
|
+
item,
|
|
652
|
+
args[0],
|
|
653
|
+
nested=nested - 1,
|
|
654
|
+
treat_var_as_type=treat_var_as_type,
|
|
655
|
+
)
|
|
656
|
+
for item in obj
|
|
571
657
|
)
|
|
572
658
|
if origin is tuple:
|
|
573
659
|
if args[-1] is Ellipsis:
|
|
574
660
|
return isinstance(obj, tuple) and all(
|
|
575
|
-
_isinstance(
|
|
661
|
+
_isinstance(
|
|
662
|
+
item,
|
|
663
|
+
args[0],
|
|
664
|
+
nested=nested - 1,
|
|
665
|
+
treat_var_as_type=treat_var_as_type,
|
|
666
|
+
)
|
|
667
|
+
for item in obj
|
|
576
668
|
)
|
|
577
669
|
return (
|
|
578
670
|
isinstance(obj, tuple)
|
|
579
671
|
and len(obj) == len(args)
|
|
580
672
|
and all(
|
|
581
|
-
_isinstance(
|
|
673
|
+
_isinstance(
|
|
674
|
+
item,
|
|
675
|
+
arg,
|
|
676
|
+
nested=nested - 1,
|
|
677
|
+
treat_var_as_type=treat_var_as_type,
|
|
678
|
+
)
|
|
582
679
|
for item, arg in zip(obj, args, strict=True)
|
|
583
680
|
)
|
|
584
681
|
)
|
|
585
682
|
if origin in (dict, Mapping, Breakpoints):
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
683
|
+
expected_class = (
|
|
684
|
+
dict
|
|
685
|
+
if origin is dict and not treat_mutable_obj_as_immutable
|
|
686
|
+
else Mapping
|
|
687
|
+
)
|
|
688
|
+
return isinstance(obj, expected_class) and all(
|
|
689
|
+
_isinstance(
|
|
690
|
+
key, args[0], nested=nested - 1, treat_var_as_type=treat_var_as_type
|
|
691
|
+
)
|
|
692
|
+
and _isinstance(
|
|
693
|
+
value,
|
|
694
|
+
args[1],
|
|
695
|
+
nested=nested - 1,
|
|
696
|
+
treat_var_as_type=treat_var_as_type,
|
|
697
|
+
)
|
|
589
698
|
for key, value in obj.items()
|
|
590
699
|
)
|
|
591
700
|
if origin is set:
|
|
592
|
-
|
|
593
|
-
|
|
701
|
+
expected_class = Sequence if treat_mutable_obj_as_immutable else set
|
|
702
|
+
return isinstance(obj, expected_class) and all(
|
|
703
|
+
_isinstance(
|
|
704
|
+
item,
|
|
705
|
+
args[0],
|
|
706
|
+
nested=nested - 1,
|
|
707
|
+
treat_var_as_type=treat_var_as_type,
|
|
708
|
+
)
|
|
709
|
+
for item in obj
|
|
594
710
|
)
|
|
595
711
|
|
|
596
712
|
if args:
|
|
597
713
|
from reflex.vars import Field
|
|
598
714
|
|
|
599
715
|
if origin is Field:
|
|
600
|
-
return _isinstance(
|
|
716
|
+
return _isinstance(
|
|
717
|
+
obj, args[0], nested=nested, treat_var_as_type=treat_var_as_type
|
|
718
|
+
)
|
|
601
719
|
|
|
602
720
|
return isinstance(obj, get_base_class(cls))
|
|
603
721
|
|
|
@@ -805,7 +923,7 @@ StateBases = get_base_class(StateVar)
|
|
|
805
923
|
StateIterBases = get_base_class(StateIterVar)
|
|
806
924
|
|
|
807
925
|
|
|
808
|
-
def safe_issubclass(cls: Type, cls_check: Type |
|
|
926
|
+
def safe_issubclass(cls: Type, cls_check: Type | tuple[Type, ...]):
|
|
809
927
|
"""Check if a class is a subclass of another class. Returns False if internal error occurs.
|
|
810
928
|
|
|
811
929
|
Args:
|
|
@@ -821,12 +939,22 @@ def safe_issubclass(cls: Type, cls_check: Type | Tuple[Type, ...]):
|
|
|
821
939
|
return False
|
|
822
940
|
|
|
823
941
|
|
|
824
|
-
def typehint_issubclass(
|
|
942
|
+
def typehint_issubclass(
|
|
943
|
+
possible_subclass: Any,
|
|
944
|
+
possible_superclass: Any,
|
|
945
|
+
*,
|
|
946
|
+
treat_mutable_superclasss_as_immutable: bool = False,
|
|
947
|
+
treat_literals_as_union_of_types: bool = True,
|
|
948
|
+
treat_any_as_subtype_of_everything: bool = False,
|
|
949
|
+
) -> bool:
|
|
825
950
|
"""Check if a type hint is a subclass of another type hint.
|
|
826
951
|
|
|
827
952
|
Args:
|
|
828
953
|
possible_subclass: The type hint to check.
|
|
829
954
|
possible_superclass: The type hint to check against.
|
|
955
|
+
treat_mutable_superclasss_as_immutable: Whether to treat target classes as immutable.
|
|
956
|
+
treat_literals_as_union_of_types: Whether to treat literals as a union of their types.
|
|
957
|
+
treat_any_as_subtype_of_everything: Whether to treat Any as a subtype of everything. This is the default behavior in Python.
|
|
830
958
|
|
|
831
959
|
Returns:
|
|
832
960
|
Whether the type hint is a subclass of the other type hint.
|
|
@@ -834,7 +962,9 @@ def typehint_issubclass(possible_subclass: Any, possible_superclass: Any) -> boo
|
|
|
834
962
|
if possible_superclass is Any:
|
|
835
963
|
return True
|
|
836
964
|
if possible_subclass is Any:
|
|
837
|
-
return
|
|
965
|
+
return treat_any_as_subtype_of_everything
|
|
966
|
+
if possible_subclass is NoReturn:
|
|
967
|
+
return True
|
|
838
968
|
|
|
839
969
|
provided_type_origin = get_origin(possible_subclass)
|
|
840
970
|
accepted_type_origin = get_origin(possible_superclass)
|
|
@@ -843,6 +973,19 @@ def typehint_issubclass(possible_subclass: Any, possible_superclass: Any) -> boo
|
|
|
843
973
|
# In this case, we are dealing with a non-generic type, so we can use issubclass
|
|
844
974
|
return issubclass(possible_subclass, possible_superclass)
|
|
845
975
|
|
|
976
|
+
if treat_literals_as_union_of_types and is_literal(possible_superclass):
|
|
977
|
+
args = get_args(possible_superclass)
|
|
978
|
+
return any(
|
|
979
|
+
typehint_issubclass(
|
|
980
|
+
possible_subclass,
|
|
981
|
+
type(arg),
|
|
982
|
+
treat_mutable_superclasss_as_immutable=treat_mutable_superclasss_as_immutable,
|
|
983
|
+
treat_literals_as_union_of_types=treat_literals_as_union_of_types,
|
|
984
|
+
treat_any_as_subtype_of_everything=treat_any_as_subtype_of_everything,
|
|
985
|
+
)
|
|
986
|
+
for arg in args
|
|
987
|
+
)
|
|
988
|
+
|
|
846
989
|
# Remove this check when Python 3.10 is the minimum supported version
|
|
847
990
|
if hasattr(types, "UnionType"):
|
|
848
991
|
provided_type_origin = (
|
|
@@ -852,28 +995,60 @@ def typehint_issubclass(possible_subclass: Any, possible_superclass: Any) -> boo
|
|
|
852
995
|
Union if accepted_type_origin is types.UnionType else accepted_type_origin
|
|
853
996
|
)
|
|
854
997
|
|
|
855
|
-
# Get type arguments (e.g., [float, int] for
|
|
998
|
+
# Get type arguments (e.g., [float, int] for dict[float, int])
|
|
856
999
|
provided_args = get_args(possible_subclass)
|
|
857
1000
|
accepted_args = get_args(possible_superclass)
|
|
858
1001
|
|
|
859
1002
|
if accepted_type_origin is Union:
|
|
860
1003
|
if provided_type_origin is not Union:
|
|
861
1004
|
return any(
|
|
862
|
-
typehint_issubclass(
|
|
1005
|
+
typehint_issubclass(
|
|
1006
|
+
possible_subclass,
|
|
1007
|
+
accepted_arg,
|
|
1008
|
+
treat_mutable_superclasss_as_immutable=treat_mutable_superclasss_as_immutable,
|
|
1009
|
+
treat_literals_as_union_of_types=treat_literals_as_union_of_types,
|
|
1010
|
+
treat_any_as_subtype_of_everything=treat_any_as_subtype_of_everything,
|
|
1011
|
+
)
|
|
863
1012
|
for accepted_arg in accepted_args
|
|
864
1013
|
)
|
|
865
1014
|
return all(
|
|
866
1015
|
any(
|
|
867
|
-
typehint_issubclass(
|
|
1016
|
+
typehint_issubclass(
|
|
1017
|
+
provided_arg,
|
|
1018
|
+
accepted_arg,
|
|
1019
|
+
treat_mutable_superclasss_as_immutable=treat_mutable_superclasss_as_immutable,
|
|
1020
|
+
treat_literals_as_union_of_types=treat_literals_as_union_of_types,
|
|
1021
|
+
treat_any_as_subtype_of_everything=treat_any_as_subtype_of_everything,
|
|
1022
|
+
)
|
|
868
1023
|
for accepted_arg in accepted_args
|
|
869
1024
|
)
|
|
870
1025
|
for provided_arg in provided_args
|
|
871
1026
|
)
|
|
1027
|
+
if provided_type_origin is Union:
|
|
1028
|
+
return all(
|
|
1029
|
+
typehint_issubclass(
|
|
1030
|
+
provided_arg,
|
|
1031
|
+
possible_superclass,
|
|
1032
|
+
treat_mutable_superclasss_as_immutable=treat_mutable_superclasss_as_immutable,
|
|
1033
|
+
treat_literals_as_union_of_types=treat_literals_as_union_of_types,
|
|
1034
|
+
treat_any_as_subtype_of_everything=treat_any_as_subtype_of_everything,
|
|
1035
|
+
)
|
|
1036
|
+
for provided_arg in provided_args
|
|
1037
|
+
)
|
|
1038
|
+
|
|
1039
|
+
provided_type_origin = provided_type_origin or possible_subclass
|
|
1040
|
+
accepted_type_origin = accepted_type_origin or possible_superclass
|
|
872
1041
|
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
accepted_type_origin or
|
|
1042
|
+
if treat_mutable_superclasss_as_immutable:
|
|
1043
|
+
if accepted_type_origin is dict:
|
|
1044
|
+
accepted_type_origin = Mapping
|
|
1045
|
+
elif accepted_type_origin is list or accepted_type_origin is set:
|
|
1046
|
+
accepted_type_origin = Sequence
|
|
1047
|
+
|
|
1048
|
+
# Check if the origin of both types is the same (e.g., list for list[int])
|
|
1049
|
+
if not safe_issubclass(
|
|
1050
|
+
provided_type_origin or possible_subclass, # pyright: ignore [reportArgumentType]
|
|
1051
|
+
accepted_type_origin or possible_superclass, # pyright: ignore [reportArgumentType]
|
|
877
1052
|
):
|
|
878
1053
|
return False
|
|
879
1054
|
|
|
@@ -881,7 +1056,13 @@ def typehint_issubclass(possible_subclass: Any, possible_superclass: Any) -> boo
|
|
|
881
1056
|
# Note this is not necessarily correct, as it doesn't check against contravariance and covariance
|
|
882
1057
|
# It also ignores when the length of the arguments is different
|
|
883
1058
|
return all(
|
|
884
|
-
typehint_issubclass(
|
|
1059
|
+
typehint_issubclass(
|
|
1060
|
+
provided_arg,
|
|
1061
|
+
accepted_arg,
|
|
1062
|
+
treat_mutable_superclasss_as_immutable=treat_mutable_superclasss_as_immutable,
|
|
1063
|
+
treat_literals_as_union_of_types=treat_literals_as_union_of_types,
|
|
1064
|
+
treat_any_as_subtype_of_everything=treat_any_as_subtype_of_everything,
|
|
1065
|
+
)
|
|
885
1066
|
for provided_arg, accepted_arg in zip(
|
|
886
1067
|
provided_args, accepted_args, strict=False
|
|
887
1068
|
)
|