reflex 0.7.14a6__py3-none-any.whl → 0.8.0a1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of reflex might be problematic. Click here for more details.
- reflex/.templates/jinja/app/rxconfig.py.jinja2 +4 -1
- reflex/.templates/jinja/web/package.json.jinja2 +1 -1
- reflex/.templates/jinja/web/pages/_app.js.jinja2 +16 -10
- 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/utils/context.js.jinja2 +25 -6
- 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 -32
- reflex/.templates/web/postcss.config.js +1 -1
- reflex/.templates/web/react-router.config.js +6 -0
- reflex/.templates/web/utils/client_side_routing.js +21 -19
- reflex/.templates/web/utils/react-theme.js +89 -0
- reflex/.templates/web/utils/state.js +155 -67
- reflex/.templates/web/vite.config.js +32 -0
- reflex/__init__.py +1 -6
- reflex/__init__.pyi +0 -4
- reflex/app.py +52 -115
- reflex/base.py +1 -87
- reflex/compiler/compiler.py +40 -3
- reflex/compiler/utils.py +54 -28
- reflex/components/__init__.py +0 -2
- reflex/components/__init__.pyi +0 -3
- reflex/components/base/__init__.py +1 -5
- reflex/components/base/__init__.pyi +4 -6
- reflex/components/base/app_wrap.pyi +5 -4
- reflex/components/base/body.pyi +5 -4
- reflex/components/base/document.py +18 -14
- reflex/components/base/document.pyi +83 -27
- reflex/components/base/error_boundary.pyi +5 -4
- reflex/components/base/fragment.pyi +5 -4
- reflex/components/base/link.pyi +9 -7
- reflex/components/base/meta.pyi +17 -13
- reflex/components/base/script.py +60 -58
- reflex/components/base/script.pyi +246 -31
- reflex/components/base/strict_mode.pyi +5 -4
- reflex/components/component.py +109 -194
- reflex/components/core/__init__.py +1 -0
- reflex/components/core/__init__.pyi +1 -0
- reflex/components/core/auto_scroll.pyi +5 -4
- reflex/components/core/banner.pyi +25 -19
- reflex/components/core/client_side_routing.py +7 -6
- reflex/components/core/client_side_routing.pyi +6 -56
- reflex/components/core/clipboard.pyi +5 -4
- reflex/components/core/debounce.py +1 -0
- reflex/components/core/debounce.pyi +5 -4
- reflex/components/core/foreach.py +3 -2
- reflex/components/core/helmet.py +14 -0
- reflex/components/{next/base.pyi → core/helmet.pyi} +10 -7
- reflex/components/core/html.pyi +5 -4
- reflex/components/core/sticky.pyi +17 -13
- reflex/components/core/upload.py +2 -1
- reflex/components/core/upload.pyi +21 -16
- reflex/components/datadisplay/code.pyi +9 -7
- reflex/components/datadisplay/dataeditor.pyi +5 -4
- reflex/components/datadisplay/shiki_code_block.pyi +13 -10
- reflex/components/dynamic.py +4 -5
- reflex/components/el/element.pyi +5 -4
- reflex/components/el/elements/base.pyi +5 -4
- reflex/components/el/elements/forms.pyi +69 -52
- reflex/components/el/elements/inline.pyi +113 -85
- reflex/components/el/elements/media.pyi +105 -79
- reflex/components/el/elements/metadata.pyi +25 -19
- reflex/components/el/elements/other.pyi +29 -22
- reflex/components/el/elements/scripts.pyi +13 -10
- reflex/components/el/elements/sectioning.pyi +61 -46
- reflex/components/el/elements/tables.pyi +41 -31
- reflex/components/el/elements/typography.pyi +61 -46
- reflex/components/field.py +175 -0
- reflex/components/gridjs/datatable.py +2 -2
- reflex/components/gridjs/datatable.pyi +11 -9
- reflex/components/lucide/icon.py +6 -2
- reflex/components/lucide/icon.pyi +15 -10
- reflex/components/markdown/markdown.pyi +5 -4
- reflex/components/moment/moment.pyi +5 -4
- reflex/components/plotly/plotly.pyi +19 -10
- reflex/components/props.py +376 -27
- reflex/components/radix/primitives/accordion.py +8 -1
- reflex/components/radix/primitives/accordion.pyi +29 -22
- reflex/components/radix/primitives/base.pyi +9 -7
- reflex/components/radix/primitives/drawer.pyi +45 -34
- reflex/components/radix/primitives/form.pyi +41 -31
- reflex/components/radix/primitives/progress.pyi +21 -16
- reflex/components/radix/primitives/slider.pyi +21 -16
- reflex/components/radix/themes/base.py +3 -3
- reflex/components/radix/themes/base.pyi +33 -25
- reflex/components/radix/themes/color_mode.pyi +13 -10
- reflex/components/radix/themes/components/alert_dialog.pyi +29 -22
- reflex/components/radix/themes/components/aspect_ratio.pyi +5 -4
- reflex/components/radix/themes/components/avatar.pyi +5 -4
- reflex/components/radix/themes/components/badge.pyi +5 -4
- reflex/components/radix/themes/components/button.pyi +5 -4
- reflex/components/radix/themes/components/callout.pyi +21 -16
- reflex/components/radix/themes/components/card.pyi +5 -4
- reflex/components/radix/themes/components/checkbox.pyi +13 -10
- reflex/components/radix/themes/components/checkbox_cards.pyi +9 -7
- reflex/components/radix/themes/components/checkbox_group.pyi +9 -7
- reflex/components/radix/themes/components/context_menu.pyi +53 -40
- reflex/components/radix/themes/components/data_list.pyi +17 -13
- reflex/components/radix/themes/components/dialog.pyi +29 -22
- reflex/components/radix/themes/components/dropdown_menu.pyi +33 -25
- reflex/components/radix/themes/components/hover_card.pyi +17 -13
- reflex/components/radix/themes/components/icon_button.pyi +5 -4
- reflex/components/radix/themes/components/inset.pyi +5 -4
- reflex/components/radix/themes/components/popover.pyi +17 -13
- reflex/components/radix/themes/components/progress.pyi +5 -4
- reflex/components/radix/themes/components/radio.pyi +5 -4
- reflex/components/radix/themes/components/radio_cards.pyi +9 -7
- reflex/components/radix/themes/components/radio_group.pyi +17 -13
- reflex/components/radix/themes/components/scroll_area.pyi +5 -4
- reflex/components/radix/themes/components/segmented_control.pyi +9 -7
- reflex/components/radix/themes/components/select.pyi +37 -28
- reflex/components/radix/themes/components/separator.pyi +5 -4
- reflex/components/radix/themes/components/skeleton.pyi +5 -4
- reflex/components/radix/themes/components/slider.pyi +5 -4
- reflex/components/radix/themes/components/spinner.pyi +5 -4
- reflex/components/radix/themes/components/switch.pyi +5 -4
- reflex/components/radix/themes/components/table.pyi +29 -22
- reflex/components/radix/themes/components/tabs.pyi +21 -16
- reflex/components/radix/themes/components/text_area.pyi +5 -4
- reflex/components/radix/themes/components/text_field.pyi +13 -10
- reflex/components/radix/themes/components/tooltip.pyi +5 -4
- reflex/components/radix/themes/layout/base.pyi +5 -4
- reflex/components/radix/themes/layout/box.pyi +5 -4
- reflex/components/radix/themes/layout/center.pyi +5 -4
- reflex/components/radix/themes/layout/container.pyi +5 -4
- reflex/components/radix/themes/layout/flex.pyi +5 -4
- reflex/components/radix/themes/layout/grid.pyi +5 -4
- reflex/components/radix/themes/layout/list.pyi +21 -16
- reflex/components/radix/themes/layout/section.pyi +5 -4
- reflex/components/radix/themes/layout/spacer.pyi +5 -4
- reflex/components/radix/themes/layout/stack.pyi +13 -10
- reflex/components/radix/themes/typography/blockquote.pyi +5 -4
- reflex/components/radix/themes/typography/code.pyi +5 -4
- reflex/components/radix/themes/typography/heading.pyi +5 -4
- reflex/components/radix/themes/typography/link.py +42 -9
- reflex/components/radix/themes/typography/link.pyi +311 -6
- reflex/components/radix/themes/typography/text.pyi +29 -22
- reflex/components/react_player/audio.pyi +5 -4
- reflex/components/react_player/react_player.pyi +5 -4
- reflex/components/react_player/video.pyi +5 -4
- reflex/components/recharts/cartesian.py +2 -1
- reflex/components/recharts/cartesian.pyi +65 -46
- reflex/components/recharts/charts.py +4 -2
- reflex/components/recharts/charts.pyi +36 -24
- reflex/components/recharts/general.pyi +24 -18
- reflex/components/recharts/polar.py +8 -4
- reflex/components/recharts/polar.pyi +16 -10
- reflex/components/recharts/recharts.pyi +9 -7
- reflex/components/sonner/toast.py +2 -2
- reflex/components/sonner/toast.pyi +7 -6
- reflex/config.py +3 -77
- reflex/constants/__init__.py +1 -0
- reflex/constants/base.py +38 -8
- reflex/constants/compiler.py +4 -2
- reflex/constants/event.py +1 -0
- reflex/constants/installer.py +23 -16
- reflex/constants/state.py +2 -0
- reflex/custom_components/custom_components.py +0 -14
- reflex/environment.py +1 -1
- reflex/event.py +178 -81
- reflex/experimental/__init__.py +0 -30
- 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 +158 -0
- reflex/plugins/sitemap.py +206 -0
- reflex/plugins/tailwind_v3.py +13 -106
- reflex/plugins/tailwind_v4.py +15 -108
- reflex/reflex.py +1 -0
- reflex/state.py +134 -140
- reflex/testing.py +57 -9
- reflex/utils/build.py +9 -69
- reflex/utils/exec.py +59 -161
- reflex/utils/export.py +1 -1
- reflex/utils/imports.py +0 -4
- reflex/utils/misc.py +28 -0
- reflex/utils/prerequisites.py +62 -59
- reflex/utils/processes.py +6 -6
- reflex/utils/pyi_generator.py +21 -9
- reflex/utils/serializers.py +14 -1
- reflex/utils/types.py +196 -61
- reflex/vars/__init__.py +2 -0
- reflex/vars/base.py +367 -134
- {reflex-0.7.14a6.dist-info → reflex-0.8.0a1.dist-info}/METADATA +12 -5
- {reflex-0.7.14a6.dist-info → reflex-0.8.0a1.dist-info}/RECORD +190 -197
- 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 → reflex-0.8.0a1.dist-info}/WHEEL +0 -0
- {reflex-0.7.14a6.dist-info → reflex-0.8.0a1.dist-info}/entry_points.txt +0 -0
- {reflex-0.7.14a6.dist-info → reflex-0.8.0a1.dist-info}/licenses/LICENSE +0 -0
reflex/state.py
CHANGED
|
@@ -13,20 +13,10 @@ import pickle
|
|
|
13
13
|
import sys
|
|
14
14
|
import typing
|
|
15
15
|
import warnings
|
|
16
|
-
from abc import ABC
|
|
17
16
|
from collections.abc import AsyncIterator, Callable, Sequence
|
|
18
17
|
from hashlib import md5
|
|
19
18
|
from types import FunctionType
|
|
20
|
-
from typing import
|
|
21
|
-
TYPE_CHECKING,
|
|
22
|
-
Any,
|
|
23
|
-
BinaryIO,
|
|
24
|
-
ClassVar,
|
|
25
|
-
TypeVar,
|
|
26
|
-
cast,
|
|
27
|
-
get_args,
|
|
28
|
-
get_type_hints,
|
|
29
|
-
)
|
|
19
|
+
from typing import TYPE_CHECKING, Any, BinaryIO, ClassVar, TypeVar, cast, get_type_hints
|
|
30
20
|
|
|
31
21
|
import pydantic.v1 as pydantic
|
|
32
22
|
from pydantic import BaseModel as BaseModelV2
|
|
@@ -38,6 +28,7 @@ from typing_extensions import Self
|
|
|
38
28
|
import reflex.istate.dynamic
|
|
39
29
|
from reflex import constants, event
|
|
40
30
|
from reflex.base import Base
|
|
31
|
+
from reflex.constants.state import FIELD_MARKER
|
|
41
32
|
from reflex.environment import PerformanceMode, environment
|
|
42
33
|
from reflex.event import (
|
|
43
34
|
BACKGROUND_TASK_MARKER,
|
|
@@ -68,21 +59,15 @@ from reflex.utils.exceptions import (
|
|
|
68
59
|
)
|
|
69
60
|
from reflex.utils.exceptions import ImmutableStateError as ImmutableStateError
|
|
70
61
|
from reflex.utils.exec import is_testing_env
|
|
71
|
-
from reflex.utils.types import
|
|
72
|
-
|
|
73
|
-
get_origin,
|
|
74
|
-
is_union,
|
|
75
|
-
true_type_for_pydantic_field,
|
|
76
|
-
value_inside_optional,
|
|
77
|
-
)
|
|
78
|
-
from reflex.vars import VarData
|
|
62
|
+
from reflex.utils.types import _isinstance, is_union, value_inside_optional
|
|
63
|
+
from reflex.vars import Field, VarData, field
|
|
79
64
|
from reflex.vars.base import (
|
|
80
65
|
ComputedVar,
|
|
81
66
|
DynamicRouteVar,
|
|
67
|
+
EvenMoreBasicBaseState,
|
|
82
68
|
Var,
|
|
83
69
|
computed_var,
|
|
84
70
|
dispatch,
|
|
85
|
-
get_unique_variable_name,
|
|
86
71
|
is_computed_var,
|
|
87
72
|
)
|
|
88
73
|
|
|
@@ -205,6 +190,21 @@ class EventHandlerSetVar(EventHandler):
|
|
|
205
190
|
)
|
|
206
191
|
object.__setattr__(self, "state_cls", state_cls)
|
|
207
192
|
|
|
193
|
+
def __hash__(self):
|
|
194
|
+
"""Get the hash of the event handler.
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
The hash of the event handler.
|
|
198
|
+
"""
|
|
199
|
+
return hash(
|
|
200
|
+
(
|
|
201
|
+
tuple(self.event_actions.items()),
|
|
202
|
+
self.fn,
|
|
203
|
+
self.state_full_name,
|
|
204
|
+
self.state_cls,
|
|
205
|
+
)
|
|
206
|
+
)
|
|
207
|
+
|
|
208
208
|
def setvar(self, var_name: str, value: Any):
|
|
209
209
|
"""Set the state variable to the value of the event.
|
|
210
210
|
|
|
@@ -255,38 +255,25 @@ if TYPE_CHECKING:
|
|
|
255
255
|
from pydantic.v1.fields import ModelField
|
|
256
256
|
|
|
257
257
|
|
|
258
|
-
def
|
|
259
|
-
"""
|
|
260
|
-
|
|
261
|
-
Args:
|
|
262
|
-
type_: The type to unwrap.
|
|
263
|
-
|
|
264
|
-
Returns:
|
|
265
|
-
The unwrapped type.
|
|
266
|
-
"""
|
|
267
|
-
from reflex.vars import Field
|
|
268
|
-
|
|
269
|
-
if get_origin(type_) is Field:
|
|
270
|
-
return get_args(type_)[0]
|
|
271
|
-
return type_
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
def get_var_for_field(cls: type[BaseState], f: ModelField):
|
|
275
|
-
"""Get a Var instance for a Pydantic field.
|
|
258
|
+
def get_var_for_field(cls: type[BaseState], name: str, f: Field) -> Var:
|
|
259
|
+
"""Get a Var instance for a state field.
|
|
276
260
|
|
|
277
261
|
Args:
|
|
278
262
|
cls: The state class.
|
|
279
|
-
|
|
263
|
+
name: The name of the field.
|
|
264
|
+
f: The Field instance.
|
|
280
265
|
|
|
281
266
|
Returns:
|
|
282
267
|
The Var instance.
|
|
283
268
|
"""
|
|
284
|
-
field_name =
|
|
269
|
+
field_name = (
|
|
270
|
+
format.format_state_name(cls.get_full_name()) + "." + name + FIELD_MARKER
|
|
271
|
+
)
|
|
285
272
|
|
|
286
273
|
return dispatch(
|
|
287
274
|
field_name=field_name,
|
|
288
|
-
var_data=VarData.from_state(cls,
|
|
289
|
-
result_var_type=
|
|
275
|
+
var_data=VarData.from_state(cls, name),
|
|
276
|
+
result_var_type=f.outer_type_,
|
|
290
277
|
)
|
|
291
278
|
|
|
292
279
|
|
|
@@ -312,7 +299,7 @@ async def _resolve_delta(delta: Delta) -> Delta:
|
|
|
312
299
|
all_base_state_classes: dict[str, None] = {}
|
|
313
300
|
|
|
314
301
|
|
|
315
|
-
class BaseState(
|
|
302
|
+
class BaseState(EvenMoreBasicBaseState):
|
|
316
303
|
"""The state of the app."""
|
|
317
304
|
|
|
318
305
|
# A map from the var name to the var.
|
|
@@ -352,31 +339,34 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
352
339
|
_potentially_dirty_states: ClassVar[set[str]] = set()
|
|
353
340
|
|
|
354
341
|
# The parent state.
|
|
355
|
-
parent_state: BaseState | None = None
|
|
342
|
+
parent_state: BaseState | None = field(default=None, is_var=False)
|
|
356
343
|
|
|
357
344
|
# The substates of the state.
|
|
358
|
-
substates: builtins.dict[str, BaseState] =
|
|
345
|
+
substates: builtins.dict[str, BaseState] = field(
|
|
346
|
+
default_factory=builtins.dict, is_var=False
|
|
347
|
+
)
|
|
359
348
|
|
|
360
349
|
# The set of dirty vars.
|
|
361
|
-
dirty_vars: set[str] = set
|
|
350
|
+
dirty_vars: set[str] = field(default_factory=set, is_var=False)
|
|
362
351
|
|
|
363
352
|
# The set of dirty substates.
|
|
364
|
-
dirty_substates: set[str] = set
|
|
353
|
+
dirty_substates: set[str] = field(default_factory=set, is_var=False)
|
|
365
354
|
|
|
366
355
|
# The routing path that triggered the state
|
|
367
|
-
router_data: builtins.dict[str, Any] =
|
|
356
|
+
router_data: builtins.dict[str, Any] = field(
|
|
357
|
+
default_factory=builtins.dict, is_var=False
|
|
358
|
+
)
|
|
368
359
|
|
|
369
360
|
# Per-instance copy of backend base variable values
|
|
370
|
-
_backend_vars: builtins.dict[str, Any] =
|
|
361
|
+
_backend_vars: builtins.dict[str, Any] = field(
|
|
362
|
+
default_factory=builtins.dict, is_var=False
|
|
363
|
+
)
|
|
371
364
|
|
|
372
365
|
# The router data for the current page
|
|
373
|
-
router: RouterData = RouterData
|
|
366
|
+
router: Field[RouterData] = field(default_factory=RouterData)
|
|
374
367
|
|
|
375
368
|
# Whether the state has ever been touched since instantiation.
|
|
376
|
-
_was_touched: bool = False
|
|
377
|
-
|
|
378
|
-
# Whether this state class is a mixin and should not be instantiated.
|
|
379
|
-
_mixin: ClassVar[bool] = False
|
|
369
|
+
_was_touched: bool = field(default=False, is_var=False)
|
|
380
370
|
|
|
381
371
|
# A special event handler for setting base vars.
|
|
382
372
|
setvar: ClassVar[EventHandler]
|
|
@@ -409,13 +399,11 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
409
399
|
"See https://reflex.dev/docs/state/ for further information."
|
|
410
400
|
)
|
|
411
401
|
raise ReflexRuntimeError(msg)
|
|
412
|
-
if
|
|
402
|
+
if self._mixin:
|
|
413
403
|
msg = f"{type(self).__name__} is a state mixin and cannot be instantiated directly."
|
|
414
404
|
raise ReflexRuntimeError(msg)
|
|
415
405
|
kwargs["parent_state"] = parent_state
|
|
416
|
-
super().__init__()
|
|
417
|
-
for name, value in kwargs.items():
|
|
418
|
-
setattr(self, name, value)
|
|
406
|
+
super().__init__(**kwargs)
|
|
419
407
|
|
|
420
408
|
# Setup the substates (for memory state manager only).
|
|
421
409
|
if init_substates:
|
|
@@ -437,14 +425,14 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
437
425
|
return f"{type(self).__name__}({self.dict()})"
|
|
438
426
|
|
|
439
427
|
@classmethod
|
|
440
|
-
def _get_computed_vars(cls) -> list[ComputedVar]:
|
|
428
|
+
def _get_computed_vars(cls) -> list[tuple[str, ComputedVar]]:
|
|
441
429
|
"""Helper function to get all computed vars of a instance.
|
|
442
430
|
|
|
443
431
|
Returns:
|
|
444
432
|
A list of computed vars.
|
|
445
433
|
"""
|
|
446
434
|
return [
|
|
447
|
-
v
|
|
435
|
+
(name, v)
|
|
448
436
|
for mixin in [*cls._mixins(), cls]
|
|
449
437
|
for name, v in mixin.__dict__.items()
|
|
450
438
|
if is_computed_var(v) and name not in cls.inherited_vars
|
|
@@ -481,8 +469,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
481
469
|
|
|
482
470
|
super().__init_subclass__(**kwargs)
|
|
483
471
|
|
|
484
|
-
cls._mixin
|
|
485
|
-
if mixin:
|
|
472
|
+
if cls._mixin:
|
|
486
473
|
return
|
|
487
474
|
|
|
488
475
|
# Handle locally-defined states for pickling.
|
|
@@ -548,13 +535,13 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
548
535
|
|
|
549
536
|
# Set the base and computed vars.
|
|
550
537
|
cls.base_vars = {
|
|
551
|
-
|
|
552
|
-
for f in cls.get_fields().
|
|
553
|
-
if
|
|
538
|
+
name: get_var_for_field(cls, name, f)
|
|
539
|
+
for name, f in cls.get_fields().items()
|
|
540
|
+
if name not in cls.get_skip_vars() and f.is_var and not name.startswith("_")
|
|
554
541
|
}
|
|
555
542
|
cls.computed_vars = {
|
|
556
|
-
|
|
557
|
-
for v in computed_vars
|
|
543
|
+
name: v._replace(merge_var_data=VarData.from_state(cls))
|
|
544
|
+
for name, v in computed_vars
|
|
558
545
|
}
|
|
559
546
|
cls.vars = {
|
|
560
547
|
**cls.inherited_vars,
|
|
@@ -564,8 +551,8 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
564
551
|
cls.event_handlers = {}
|
|
565
552
|
|
|
566
553
|
# Setup the base vars at the class level.
|
|
567
|
-
for prop in cls.base_vars.
|
|
568
|
-
cls._init_var(prop)
|
|
554
|
+
for name, prop in cls.base_vars.items():
|
|
555
|
+
cls._init_var(name, prop)
|
|
569
556
|
|
|
570
557
|
# Set up the event handlers.
|
|
571
558
|
events = {
|
|
@@ -583,8 +570,8 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
583
570
|
newcv = value._replace(fget=fget, _var_data=VarData.from_state(cls))
|
|
584
571
|
# cleanup refs to mixin cls in var_data
|
|
585
572
|
setattr(cls, name, newcv)
|
|
586
|
-
cls.computed_vars[
|
|
587
|
-
cls.vars[
|
|
573
|
+
cls.computed_vars[name] = newcv
|
|
574
|
+
cls.vars[name] = newcv
|
|
588
575
|
continue
|
|
589
576
|
if types.is_backend_base_variable(name, mixin_cls):
|
|
590
577
|
cls.backend_vars[name] = copy.deepcopy(value)
|
|
@@ -687,9 +674,16 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
687
674
|
|
|
688
675
|
of_type = of_type or Component
|
|
689
676
|
|
|
690
|
-
unique_var_name =
|
|
677
|
+
unique_var_name = (
|
|
678
|
+
("dynamic_" + f.__module__ + "_" + f.__qualname__)
|
|
679
|
+
.replace("<", "")
|
|
680
|
+
.replace(">", "")
|
|
681
|
+
.replace(".", "_")
|
|
682
|
+
)
|
|
683
|
+
|
|
684
|
+
while unique_var_name in cls.vars:
|
|
685
|
+
unique_var_name += "_"
|
|
691
686
|
|
|
692
|
-
@computed_var(_js_expr=unique_var_name, return_type=of_type)
|
|
693
687
|
def computed_var_func(state: Self):
|
|
694
688
|
result = f(state)
|
|
695
689
|
|
|
@@ -701,10 +695,16 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
701
695
|
|
|
702
696
|
return result
|
|
703
697
|
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
698
|
+
computed_var_func.__name__ = unique_var_name
|
|
699
|
+
|
|
700
|
+
computed_var_func_arg = computed_var(return_type=of_type, cache=False)(
|
|
701
|
+
computed_var_func
|
|
702
|
+
)
|
|
703
|
+
|
|
704
|
+
setattr(cls, unique_var_name, computed_var_func_arg)
|
|
705
|
+
cls.computed_vars[unique_var_name] = computed_var_func_arg
|
|
706
|
+
cls.vars[unique_var_name] = computed_var_func_arg
|
|
707
|
+
cls._update_substate_inherited_vars({unique_var_name: computed_var_func_arg})
|
|
708
708
|
cls._always_dirty_computed_vars.add(unique_var_name)
|
|
709
709
|
|
|
710
710
|
return getattr(cls, unique_var_name)
|
|
@@ -842,8 +842,8 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
842
842
|
Raises:
|
|
843
843
|
ComputedVarShadowsBaseVarsError: When a computed var shadows a base var.
|
|
844
844
|
"""
|
|
845
|
-
for computed_var_ in cls._get_computed_vars():
|
|
846
|
-
if
|
|
845
|
+
for name, computed_var_ in cls._get_computed_vars():
|
|
846
|
+
if name in cls.__annotations__:
|
|
847
847
|
msg = f"The computed var name `{computed_var_._js_expr}` shadows a base var in {cls.__module__}.{cls.__name__}; use a different name instead"
|
|
848
848
|
raise ComputedVarShadowsBaseVarsError(msg)
|
|
849
849
|
|
|
@@ -898,7 +898,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
898
898
|
if issubclass(base, BaseState) and base is not BaseState and not base._mixin
|
|
899
899
|
]
|
|
900
900
|
if len(parent_states) >= 2:
|
|
901
|
-
msg = f"Only one parent state is allowed {parent_states}."
|
|
901
|
+
msg = f"Only one parent state of is allowed. Found {parent_states} parents of {cls}."
|
|
902
902
|
raise ValueError(msg)
|
|
903
903
|
# The first non-mixin state in the mro is our parent.
|
|
904
904
|
for base in cls.mro()[1:]:
|
|
@@ -1016,10 +1016,11 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
1016
1016
|
)
|
|
1017
1017
|
|
|
1018
1018
|
@classmethod
|
|
1019
|
-
def _init_var(cls, prop: Var):
|
|
1019
|
+
def _init_var(cls, name: str, prop: Var):
|
|
1020
1020
|
"""Initialize a variable.
|
|
1021
1021
|
|
|
1022
1022
|
Args:
|
|
1023
|
+
name: The name of the variable
|
|
1023
1024
|
prop: The variable to initialize
|
|
1024
1025
|
|
|
1025
1026
|
Raises:
|
|
@@ -1036,10 +1037,10 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
1036
1037
|
f'Found var "{prop._js_expr}" with type {prop._var_type}.'
|
|
1037
1038
|
)
|
|
1038
1039
|
raise VarTypeError(msg)
|
|
1039
|
-
cls._set_var(prop)
|
|
1040
|
+
cls._set_var(name, prop)
|
|
1040
1041
|
if cls.is_user_defined() and get_config().state_auto_setters:
|
|
1041
|
-
cls._create_setter(prop)
|
|
1042
|
-
cls._set_default_value(prop)
|
|
1042
|
+
cls._create_setter(name, prop)
|
|
1043
|
+
cls._set_default_value(name, prop)
|
|
1043
1044
|
|
|
1044
1045
|
@classmethod
|
|
1045
1046
|
def add_var(cls, name: str, type_: Any, default_value: Any = None):
|
|
@@ -1062,15 +1063,18 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
1062
1063
|
|
|
1063
1064
|
# create the variable based on name and type
|
|
1064
1065
|
var = Var(
|
|
1065
|
-
_js_expr=format.format_state_name(cls.get_full_name())
|
|
1066
|
+
_js_expr=format.format_state_name(cls.get_full_name())
|
|
1067
|
+
+ "."
|
|
1068
|
+
+ name
|
|
1069
|
+
+ FIELD_MARKER,
|
|
1066
1070
|
_var_type=type_,
|
|
1067
1071
|
_var_data=VarData.from_state(cls, name),
|
|
1068
1072
|
).guess_type()
|
|
1069
1073
|
|
|
1070
1074
|
# add the pydantic field dynamically (must be done before _init_var)
|
|
1071
|
-
cls.add_field(var, default_value)
|
|
1075
|
+
cls.add_field(name, var, default_value)
|
|
1072
1076
|
|
|
1073
|
-
cls._init_var(var)
|
|
1077
|
+
cls._init_var(name, var)
|
|
1074
1078
|
|
|
1075
1079
|
# update the internal dicts so the new variable is correctly handled
|
|
1076
1080
|
cls.base_vars.update({name: var})
|
|
@@ -1084,13 +1088,14 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
1084
1088
|
cls._init_var_dependency_dicts()
|
|
1085
1089
|
|
|
1086
1090
|
@classmethod
|
|
1087
|
-
def _set_var(cls, prop: Var):
|
|
1091
|
+
def _set_var(cls, name: str, prop: Var):
|
|
1088
1092
|
"""Set the var as a class member.
|
|
1089
1093
|
|
|
1090
1094
|
Args:
|
|
1095
|
+
name: The name of the var.
|
|
1091
1096
|
prop: The var instance to set.
|
|
1092
1097
|
"""
|
|
1093
|
-
setattr(cls,
|
|
1098
|
+
setattr(cls, name, prop)
|
|
1094
1099
|
|
|
1095
1100
|
@classmethod
|
|
1096
1101
|
def _create_event_handler(cls, fn: Any):
|
|
@@ -1110,38 +1115,31 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
1110
1115
|
cls.setvar = cls.event_handlers["setvar"] = EventHandlerSetVar(state_cls=cls)
|
|
1111
1116
|
|
|
1112
1117
|
@classmethod
|
|
1113
|
-
def _create_setter(cls, prop: Var):
|
|
1118
|
+
def _create_setter(cls, name: str, prop: Var):
|
|
1114
1119
|
"""Create a setter for the var.
|
|
1115
1120
|
|
|
1116
1121
|
Args:
|
|
1122
|
+
name: The name of the var.
|
|
1117
1123
|
prop: The var to create a setter for.
|
|
1118
1124
|
"""
|
|
1119
|
-
setter_name =
|
|
1125
|
+
setter_name = Var._get_setter_name_for_name(name)
|
|
1120
1126
|
if setter_name not in cls.__dict__:
|
|
1121
|
-
event_handler = cls._create_event_handler(prop._get_setter())
|
|
1127
|
+
event_handler = cls._create_event_handler(prop._get_setter(name))
|
|
1122
1128
|
cls.event_handlers[setter_name] = event_handler
|
|
1123
1129
|
setattr(cls, setter_name, event_handler)
|
|
1124
1130
|
|
|
1125
1131
|
@classmethod
|
|
1126
|
-
def _set_default_value(cls, prop: Var):
|
|
1132
|
+
def _set_default_value(cls, name: str, prop: Var):
|
|
1127
1133
|
"""Set the default value for the var.
|
|
1128
1134
|
|
|
1129
1135
|
Args:
|
|
1136
|
+
name: The name of the var.
|
|
1130
1137
|
prop: The var to set the default value for.
|
|
1131
1138
|
"""
|
|
1132
1139
|
# Get the pydantic field for the var.
|
|
1133
|
-
field = cls.get_fields()[
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
if default_value is not None:
|
|
1137
|
-
field.required = False
|
|
1138
|
-
field.default = default_value
|
|
1139
|
-
if (
|
|
1140
|
-
not field.required
|
|
1141
|
-
and field.default is None
|
|
1142
|
-
and field.default_factory is None
|
|
1143
|
-
and not types.is_optional(prop._var_type)
|
|
1144
|
-
):
|
|
1140
|
+
field = cls.get_fields()[name]
|
|
1141
|
+
|
|
1142
|
+
if field.default is None and not types.is_optional(prop._var_type):
|
|
1145
1143
|
# Ensure frontend uses null coalescing when accessing.
|
|
1146
1144
|
object.__setattr__(prop, "_var_type", prop._var_type | None)
|
|
1147
1145
|
|
|
@@ -1160,7 +1158,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
1160
1158
|
return getattr(cls, name)
|
|
1161
1159
|
except AttributeError:
|
|
1162
1160
|
try:
|
|
1163
|
-
return
|
|
1161
|
+
return types.get_default_value_for_type(annotation_value)
|
|
1164
1162
|
except TypeError:
|
|
1165
1163
|
pass
|
|
1166
1164
|
return None
|
|
@@ -1215,12 +1213,16 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
1215
1213
|
def inner_func(self: BaseState) -> str:
|
|
1216
1214
|
return self.router.page.params.get(param, "")
|
|
1217
1215
|
|
|
1216
|
+
inner_func.__name__ = param
|
|
1217
|
+
|
|
1218
1218
|
return inner_func
|
|
1219
1219
|
|
|
1220
1220
|
def arglist_factory(param: str):
|
|
1221
1221
|
def inner_func(self: BaseState) -> list[str]:
|
|
1222
1222
|
return self.router.page.params.get(param, [])
|
|
1223
1223
|
|
|
1224
|
+
inner_func.__name__ = param
|
|
1225
|
+
|
|
1224
1226
|
return inner_func
|
|
1225
1227
|
|
|
1226
1228
|
dynamic_vars = {}
|
|
@@ -1235,8 +1237,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
1235
1237
|
fget=func,
|
|
1236
1238
|
auto_deps=False,
|
|
1237
1239
|
deps=["router"],
|
|
1238
|
-
|
|
1239
|
-
_var_data=VarData.from_state(cls),
|
|
1240
|
+
_var_data=VarData.from_state(cls, param),
|
|
1240
1241
|
)
|
|
1241
1242
|
setattr(cls, param, dynamic_vars[param])
|
|
1242
1243
|
|
|
@@ -1276,10 +1277,6 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
1276
1277
|
Returns:
|
|
1277
1278
|
The value of the var.
|
|
1278
1279
|
"""
|
|
1279
|
-
# If the state hasn't been initialized yet, return the default value.
|
|
1280
|
-
if not super().__getattribute__("__dict__"):
|
|
1281
|
-
return super().__getattribute__(name)
|
|
1282
|
-
|
|
1283
1280
|
# Fast path for dunder
|
|
1284
1281
|
if name.startswith("__"):
|
|
1285
1282
|
return super().__getattribute__(name)
|
|
@@ -1306,7 +1303,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
1306
1303
|
fn.__qualname__ = handler.fn.__qualname__
|
|
1307
1304
|
return fn
|
|
1308
1305
|
|
|
1309
|
-
backend_vars = super().__getattribute__("_backend_vars")
|
|
1306
|
+
backend_vars = super().__getattribute__("_backend_vars") or {}
|
|
1310
1307
|
if name in backend_vars:
|
|
1311
1308
|
value = backend_vars[name]
|
|
1312
1309
|
else:
|
|
@@ -1370,9 +1367,8 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
1370
1367
|
|
|
1371
1368
|
fields = self.get_fields()
|
|
1372
1369
|
|
|
1373
|
-
if name
|
|
1374
|
-
|
|
1375
|
-
field_type = _unwrap_field_type(true_type_for_pydantic_field(field))
|
|
1370
|
+
if (field := fields.get(name)) is not None and field.is_var:
|
|
1371
|
+
field_type = field.outer_type_
|
|
1376
1372
|
if not _isinstance(value, field_type, nested=1, treat_var_as_type=False):
|
|
1377
1373
|
console.error(
|
|
1378
1374
|
f"Expected field '{type(self).__name__}.{name}' to receive type '{escape(str(field_type))}',"
|
|
@@ -1380,7 +1376,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
1380
1376
|
)
|
|
1381
1377
|
|
|
1382
1378
|
# Set the attribute.
|
|
1383
|
-
|
|
1379
|
+
object.__setattr__(self, name, value)
|
|
1384
1380
|
|
|
1385
1381
|
# Add the var to the dirty list.
|
|
1386
1382
|
if name in self.base_vars:
|
|
@@ -1969,7 +1965,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
1969
1965
|
)
|
|
1970
1966
|
|
|
1971
1967
|
subdelta: dict[str, Any] = {
|
|
1972
|
-
prop: self.get_value(prop)
|
|
1968
|
+
prop + FIELD_MARKER: self.get_value(prop)
|
|
1973
1969
|
for prop in delta_vars
|
|
1974
1970
|
if not types.is_backend_base_variable(prop, type(self))
|
|
1975
1971
|
}
|
|
@@ -2058,7 +2054,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
2058
2054
|
Returns:
|
|
2059
2055
|
The value of the field.
|
|
2060
2056
|
"""
|
|
2061
|
-
value =
|
|
2057
|
+
value = getattr(self, key)
|
|
2062
2058
|
if isinstance(value, MutableProxy):
|
|
2063
2059
|
return value.__wrapped__
|
|
2064
2060
|
return value
|
|
@@ -2104,7 +2100,9 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
2104
2100
|
computed_vars = {}
|
|
2105
2101
|
variables = {**base_vars, **computed_vars}
|
|
2106
2102
|
d = {
|
|
2107
|
-
self.get_full_name(): {
|
|
2103
|
+
self.get_full_name(): {
|
|
2104
|
+
k + FIELD_MARKER: variables[k] for k in sorted(variables)
|
|
2105
|
+
},
|
|
2108
2106
|
}
|
|
2109
2107
|
for substate_d in [
|
|
2110
2108
|
v.dict(include_computed=include_computed, initial=initial, **kwargs)
|
|
@@ -2147,19 +2145,19 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
2147
2145
|
Returns:
|
|
2148
2146
|
The state dict for serialization.
|
|
2149
2147
|
"""
|
|
2150
|
-
state =
|
|
2151
|
-
state
|
|
2152
|
-
if state
|
|
2148
|
+
state = self.__dict__
|
|
2149
|
+
state = state.copy()
|
|
2150
|
+
if state.get("parent_state") is not None:
|
|
2153
2151
|
# Do not serialize router data in substates (only the root state).
|
|
2154
|
-
state
|
|
2155
|
-
state
|
|
2152
|
+
state.pop("router", None)
|
|
2153
|
+
state.pop("router_data", None)
|
|
2156
2154
|
# Never serialize parent_state or substates.
|
|
2157
|
-
state
|
|
2158
|
-
state
|
|
2159
|
-
state
|
|
2155
|
+
state.pop("parent_state", None)
|
|
2156
|
+
state.pop("substates", None)
|
|
2157
|
+
state.pop("_was_touched", None)
|
|
2160
2158
|
# Remove all inherited vars.
|
|
2161
2159
|
for inherited_var_name in self.inherited_vars:
|
|
2162
|
-
state
|
|
2160
|
+
state.pop(inherited_var_name, None)
|
|
2163
2161
|
return state
|
|
2164
2162
|
|
|
2165
2163
|
def __setstate__(self, state: dict[str, Any]):
|
|
@@ -2170,9 +2168,10 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
2170
2168
|
Args:
|
|
2171
2169
|
state: The state dict for deserialization.
|
|
2172
2170
|
"""
|
|
2173
|
-
state["
|
|
2174
|
-
state["
|
|
2175
|
-
|
|
2171
|
+
state["parent_state"] = None
|
|
2172
|
+
state["substates"] = {}
|
|
2173
|
+
for key, value in state.items():
|
|
2174
|
+
object.__setattr__(self, key, value)
|
|
2176
2175
|
|
|
2177
2176
|
def _check_state_size(
|
|
2178
2177
|
self,
|
|
@@ -2213,17 +2212,11 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
2213
2212
|
|
|
2214
2213
|
def _field_tuple(
|
|
2215
2214
|
field_name: str,
|
|
2216
|
-
) -> tuple[str,
|
|
2215
|
+
) -> tuple[str, Any, Any]:
|
|
2217
2216
|
model_field = cls.__fields__[field_name]
|
|
2218
2217
|
return (
|
|
2219
2218
|
field_name,
|
|
2220
|
-
model_field.name,
|
|
2221
2219
|
_serialize_type(model_field.type_),
|
|
2222
|
-
(
|
|
2223
|
-
model_field.required
|
|
2224
|
-
if isinstance(model_field.required, bool)
|
|
2225
|
-
else None
|
|
2226
|
-
),
|
|
2227
2220
|
(model_field.default if is_serializable(model_field.default) else None),
|
|
2228
2221
|
)
|
|
2229
2222
|
|
|
@@ -2430,6 +2423,7 @@ class UpdateVarsInternalState(State):
|
|
|
2430
2423
|
"""
|
|
2431
2424
|
for var, value in vars.items():
|
|
2432
2425
|
state_name, _, var_name = var.rpartition(".")
|
|
2426
|
+
var_name = var_name.removesuffix(FIELD_MARKER)
|
|
2433
2427
|
var_state_cls = State.get_class_substate(state_name)
|
|
2434
2428
|
if var_state_cls._is_client_storage(var_name):
|
|
2435
2429
|
var_state = await self.get_state(var_state_cls)
|
|
@@ -2518,7 +2512,7 @@ class ComponentState(State, mixin=True):
|
|
|
2518
2512
|
Raises:
|
|
2519
2513
|
ReflexRuntimeError: If the ComponentState is initialized directly.
|
|
2520
2514
|
"""
|
|
2521
|
-
if
|
|
2515
|
+
if self._mixin:
|
|
2522
2516
|
raise ReflexRuntimeError(
|
|
2523
2517
|
f"{ComponentState.__name__} {type(self).__name__} is not meant to be initialized directly. "
|
|
2524
2518
|
+ "Use the `create` method to create a new instance and access the state via the `State` attribute."
|