reflex 0.7.13a1__py3-none-any.whl → 0.7.14__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/apps/blank/code/blank.py +0 -2
- reflex/app.py +100 -92
- reflex/app_mixins/lifespan.py +2 -3
- reflex/app_mixins/middleware.py +1 -0
- reflex/app_mixins/mixin.py +0 -1
- reflex/assets.py +7 -4
- reflex/base.py +3 -2
- reflex/compiler/compiler.py +79 -65
- reflex/compiler/utils.py +8 -6
- reflex/components/base/app_wrap.pyi +0 -1
- reflex/components/base/bare.py +22 -12
- reflex/components/base/body.pyi +0 -1
- reflex/components/base/document.pyi +0 -5
- reflex/components/base/error_boundary.pyi +0 -1
- reflex/components/base/fragment.pyi +0 -1
- reflex/components/base/head.pyi +0 -2
- reflex/components/base/link.pyi +0 -2
- reflex/components/base/meta.py +2 -1
- reflex/components/base/meta.pyi +0 -4
- reflex/components/base/script.py +2 -1
- reflex/components/base/script.pyi +0 -1
- reflex/components/base/strict_mode.pyi +0 -1
- reflex/components/component.py +85 -45
- reflex/components/core/auto_scroll.pyi +0 -1
- reflex/components/core/banner.py +1 -1
- reflex/components/core/banner.pyi +0 -6
- reflex/components/core/breakpoints.py +9 -11
- reflex/components/core/client_side_routing.pyi +0 -2
- reflex/components/core/clipboard.pyi +0 -1
- reflex/components/core/colors.py +10 -7
- reflex/components/core/cond.py +4 -2
- reflex/components/core/debounce.py +5 -3
- reflex/components/core/debounce.pyi +0 -1
- reflex/components/core/foreach.py +8 -6
- reflex/components/core/html.py +3 -3
- reflex/components/core/html.pyi +0 -1
- reflex/components/core/match.py +19 -17
- reflex/components/core/sticky.pyi +0 -4
- reflex/components/core/upload.py +1 -1
- reflex/components/core/upload.pyi +0 -5
- reflex/components/datadisplay/code.py +1 -2
- reflex/components/datadisplay/code.pyi +0 -2
- reflex/components/datadisplay/dataeditor.py +7 -10
- reflex/components/datadisplay/dataeditor.pyi +0 -1
- reflex/components/datadisplay/logo.py +3 -4
- reflex/components/datadisplay/shiki_code_block.py +8 -11
- reflex/components/datadisplay/shiki_code_block.pyi +0 -3
- reflex/components/dynamic.py +2 -3
- reflex/components/el/__init__.pyi +2 -0
- reflex/components/el/element.pyi +0 -1
- reflex/components/el/elements/__init__.py +1 -0
- reflex/components/el/elements/__init__.pyi +3 -0
- reflex/components/el/elements/base.pyi +0 -1
- reflex/components/el/elements/forms.py +14 -15
- reflex/components/el/elements/forms.pyi +15 -32
- reflex/components/el/elements/inline.pyi +0 -28
- reflex/components/el/elements/media.py +26 -0
- reflex/components/el/elements/media.pyi +259 -25
- reflex/components/el/elements/metadata.py +0 -1
- reflex/components/el/elements/metadata.pyi +0 -6
- reflex/components/el/elements/other.pyi +0 -7
- reflex/components/el/elements/scripts.pyi +0 -3
- reflex/components/el/elements/sectioning.pyi +0 -15
- reflex/components/el/elements/tables.pyi +0 -10
- reflex/components/el/elements/typography.pyi +0 -15
- reflex/components/gridjs/datatable.py +10 -13
- reflex/components/gridjs/datatable.pyi +0 -2
- reflex/components/lucide/icon.py +10 -9
- reflex/components/lucide/icon.pyi +0 -3
- reflex/components/markdown/markdown.py +6 -8
- reflex/components/markdown/markdown.pyi +0 -1
- reflex/components/moment/moment.pyi +0 -1
- reflex/components/next/base.py +0 -2
- reflex/components/next/base.pyi +0 -3
- reflex/components/next/image.pyi +0 -1
- reflex/components/next/link.pyi +0 -1
- reflex/components/next/video.pyi +0 -1
- reflex/components/plotly/plotly.pyi +0 -9
- reflex/components/props.py +4 -3
- reflex/components/radix/primitives/accordion.py +1 -1
- reflex/components/radix/primitives/accordion.pyi +0 -7
- reflex/components/radix/primitives/base.py +1 -3
- reflex/components/radix/primitives/base.pyi +0 -2
- reflex/components/radix/primitives/drawer.py +1 -1
- reflex/components/radix/primitives/drawer.pyi +0 -11
- reflex/components/radix/primitives/form.py +5 -9
- reflex/components/radix/primitives/form.pyi +0 -12
- reflex/components/radix/primitives/progress.py +1 -1
- reflex/components/radix/primitives/progress.pyi +0 -5
- reflex/components/radix/primitives/slider.py +1 -1
- reflex/components/radix/primitives/slider.pyi +0 -5
- reflex/components/radix/themes/base.pyi +0 -8
- reflex/components/radix/themes/color_mode.pyi +0 -3
- reflex/components/radix/themes/components/alert_dialog.py +4 -2
- reflex/components/radix/themes/components/alert_dialog.pyi +4 -9
- reflex/components/radix/themes/components/aspect_ratio.py +1 -2
- reflex/components/radix/themes/components/aspect_ratio.pyi +1 -3
- reflex/components/radix/themes/components/avatar.py +5 -2
- reflex/components/radix/themes/components/avatar.pyi +1 -3
- reflex/components/radix/themes/components/badge.py +5 -2
- reflex/components/radix/themes/components/badge.pyi +1 -3
- reflex/components/radix/themes/components/button.py +2 -3
- reflex/components/radix/themes/components/button.pyi +1 -3
- reflex/components/radix/themes/components/callout.py +1 -2
- reflex/components/radix/themes/components/callout.pyi +1 -7
- reflex/components/radix/themes/components/card.py +1 -2
- reflex/components/radix/themes/components/card.pyi +1 -3
- reflex/components/radix/themes/components/checkbox.py +7 -4
- reflex/components/radix/themes/components/checkbox.pyi +1 -5
- reflex/components/radix/themes/components/checkbox_cards.py +1 -2
- reflex/components/radix/themes/components/checkbox_cards.pyi +1 -4
- reflex/components/radix/themes/components/checkbox_group.py +1 -2
- reflex/components/radix/themes/components/checkbox_group.pyi +1 -4
- reflex/components/radix/themes/components/context_menu.py +1 -1
- reflex/components/radix/themes/components/context_menu.pyi +1 -14
- reflex/components/radix/themes/components/data_list.py +1 -2
- reflex/components/radix/themes/components/data_list.pyi +1 -6
- reflex/components/radix/themes/components/dialog.py +4 -2
- reflex/components/radix/themes/components/dialog.pyi +4 -9
- reflex/components/radix/themes/components/dropdown_menu.py +5 -2
- reflex/components/radix/themes/components/dropdown_menu.pyi +4 -10
- reflex/components/radix/themes/components/hover_card.py +4 -2
- reflex/components/radix/themes/components/hover_card.pyi +4 -6
- reflex/components/radix/themes/components/icon_button.py +7 -8
- reflex/components/radix/themes/components/icon_button.pyi +1 -3
- reflex/components/radix/themes/components/inset.py +1 -2
- reflex/components/radix/themes/components/inset.pyi +1 -3
- reflex/components/radix/themes/components/popover.py +4 -2
- reflex/components/radix/themes/components/popover.pyi +4 -6
- reflex/components/radix/themes/components/progress.py +1 -2
- reflex/components/radix/themes/components/progress.pyi +1 -3
- reflex/components/radix/themes/components/radio.py +1 -2
- reflex/components/radix/themes/components/radio.pyi +1 -3
- reflex/components/radix/themes/components/radio_cards.py +1 -2
- reflex/components/radix/themes/components/radio_cards.pyi +1 -4
- reflex/components/radix/themes/components/radio_group.py +7 -5
- reflex/components/radix/themes/components/radio_group.pyi +1 -6
- reflex/components/radix/themes/components/scroll_area.py +1 -2
- reflex/components/radix/themes/components/scroll_area.pyi +1 -3
- reflex/components/radix/themes/components/segmented_control.py +1 -2
- reflex/components/radix/themes/components/segmented_control.pyi +1 -4
- reflex/components/radix/themes/components/select.py +5 -2
- reflex/components/radix/themes/components/select.pyi +1 -11
- reflex/components/radix/themes/components/separator.py +1 -2
- reflex/components/radix/themes/components/separator.pyi +1 -3
- reflex/components/radix/themes/components/skeleton.py +1 -2
- reflex/components/radix/themes/components/skeleton.pyi +1 -3
- reflex/components/radix/themes/components/slider.py +1 -2
- reflex/components/radix/themes/components/slider.pyi +1 -3
- reflex/components/radix/themes/components/spinner.py +1 -2
- reflex/components/radix/themes/components/spinner.pyi +1 -3
- reflex/components/radix/themes/components/switch.py +1 -2
- reflex/components/radix/themes/components/switch.pyi +1 -3
- reflex/components/radix/themes/components/table.py +1 -2
- reflex/components/radix/themes/components/table.pyi +1 -9
- reflex/components/radix/themes/components/tabs.py +1 -2
- reflex/components/radix/themes/components/tabs.pyi +1 -7
- reflex/components/radix/themes/components/text_area.py +5 -2
- reflex/components/radix/themes/components/text_area.pyi +2 -4
- reflex/components/radix/themes/components/text_field.py +5 -2
- reflex/components/radix/themes/components/text_field.pyi +1 -5
- reflex/components/radix/themes/components/tooltip.py +1 -2
- reflex/components/radix/themes/components/tooltip.pyi +1 -3
- reflex/components/radix/themes/layout/base.py +5 -2
- reflex/components/radix/themes/layout/base.pyi +5 -3
- reflex/components/radix/themes/layout/box.py +1 -2
- reflex/components/radix/themes/layout/box.pyi +1 -3
- reflex/components/radix/themes/layout/center.pyi +0 -1
- reflex/components/radix/themes/layout/container.py +1 -2
- reflex/components/radix/themes/layout/container.pyi +1 -3
- reflex/components/radix/themes/layout/flex.py +6 -2
- reflex/components/radix/themes/layout/flex.pyi +1 -3
- reflex/components/radix/themes/layout/grid.py +6 -2
- reflex/components/radix/themes/layout/grid.pyi +1 -3
- reflex/components/radix/themes/layout/list.py +2 -1
- reflex/components/radix/themes/layout/list.pyi +0 -5
- reflex/components/radix/themes/layout/section.py +1 -2
- reflex/components/radix/themes/layout/section.pyi +1 -3
- reflex/components/radix/themes/layout/spacer.pyi +0 -1
- reflex/components/radix/themes/layout/stack.py +1 -1
- reflex/components/radix/themes/layout/stack.pyi +0 -3
- reflex/components/radix/themes/typography/blockquote.py +1 -1
- reflex/components/radix/themes/typography/blockquote.pyi +1 -3
- reflex/components/radix/themes/typography/code.py +5 -1
- reflex/components/radix/themes/typography/code.pyi +1 -3
- reflex/components/radix/themes/typography/heading.py +1 -1
- reflex/components/radix/themes/typography/heading.pyi +1 -3
- reflex/components/radix/themes/typography/link.py +3 -2
- reflex/components/radix/themes/typography/link.pyi +1 -3
- reflex/components/radix/themes/typography/text.py +1 -1
- reflex/components/radix/themes/typography/text.pyi +1 -9
- reflex/components/react_player/audio.py +0 -2
- reflex/components/react_player/audio.pyi +0 -3
- reflex/components/react_player/react_player.pyi +0 -1
- reflex/components/react_player/video.py +0 -2
- reflex/components/react_player/video.pyi +0 -3
- reflex/components/recharts/__init__.py +1 -1
- reflex/components/recharts/__init__.pyi +1 -1
- reflex/components/recharts/cartesian.py +20 -25
- reflex/components/recharts/cartesian.pyi +20 -37
- reflex/components/recharts/charts.py +2 -1
- reflex/components/recharts/charts.pyi +0 -12
- reflex/components/recharts/general.pyi +0 -6
- reflex/components/recharts/polar.py +5 -4
- reflex/components/recharts/polar.pyi +4 -10
- reflex/components/recharts/recharts.py +12 -10
- reflex/components/recharts/recharts.pyi +10 -11
- reflex/components/sonner/toast.py +2 -2
- reflex/components/sonner/toast.pyi +0 -2
- reflex/components/suneditor/editor.py +2 -1
- reflex/components/suneditor/editor.pyi +0 -1
- reflex/components/tags/iter_tag.py +4 -2
- reflex/config.py +41 -615
- reflex/constants/base.py +6 -6
- reflex/constants/compiler.py +8 -6
- reflex/constants/installer.py +25 -16
- reflex/custom_components/custom_components.py +1 -2
- reflex/environment.py +606 -0
- reflex/event.py +58 -60
- reflex/experimental/__init__.py +2 -2
- reflex/experimental/client_state.py +9 -4
- reflex/experimental/layout.pyi +0 -5
- reflex/istate/manager.py +17 -20
- reflex/istate/proxy.py +19 -12
- reflex/model.py +8 -5
- reflex/plugins/base.py +8 -0
- reflex/plugins/tailwind_v3.py +8 -0
- reflex/plugins/tailwind_v4.py +9 -0
- reflex/reflex.py +11 -12
- reflex/route.py +7 -9
- reflex/state.py +67 -71
- reflex/style.py +3 -1
- reflex/testing.py +49 -30
- reflex/utils/build.py +2 -1
- reflex/utils/console.py +70 -17
- reflex/utils/exec.py +113 -39
- reflex/utils/export.py +2 -1
- reflex/utils/format.py +21 -24
- reflex/utils/imports.py +4 -3
- reflex/utils/lazy_loader.py +3 -3
- reflex/utils/misc.py +2 -1
- reflex/utils/net.py +2 -2
- reflex/utils/path_ops.py +4 -2
- reflex/utils/prerequisites.py +69 -39
- reflex/utils/processes.py +5 -7
- reflex/utils/pyi_generator.py +46 -41
- reflex/utils/redir.py +1 -1
- reflex/utils/registry.py +1 -1
- reflex/utils/serializers.py +4 -4
- reflex/utils/telemetry.py +36 -3
- reflex/utils/types.py +16 -13
- reflex/vars/base.py +96 -109
- reflex/vars/datetime.py +2 -1
- reflex/vars/dep_tracking.py +19 -28
- reflex/vars/number.py +6 -7
- reflex/vars/object.py +5 -6
- reflex/vars/sequence.py +11 -11
- {reflex-0.7.13a1.dist-info → reflex-0.7.14.dist-info}/METADATA +1 -1
- reflex-0.7.14.dist-info/RECORD +408 -0
- reflex-0.7.13a1.dist-info/RECORD +0 -407
- {reflex-0.7.13a1.dist-info → reflex-0.7.14.dist-info}/WHEEL +0 -0
- {reflex-0.7.13a1.dist-info → reflex-0.7.14.dist-info}/entry_points.txt +0 -0
- {reflex-0.7.13a1.dist-info → reflex-0.7.14.dist-info}/licenses/LICENSE +0 -0
reflex/event.py
CHANGED
|
@@ -228,9 +228,8 @@ class EventHandler(EventActionsMixin):
|
|
|
228
228
|
),
|
|
229
229
|
Unset,
|
|
230
230
|
):
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
)
|
|
231
|
+
msg = f"Event handler {self.fn.__name__} received repeated argument {repeated_arg}."
|
|
232
|
+
raise EventHandlerTypeError(msg)
|
|
234
233
|
|
|
235
234
|
if not isinstance(
|
|
236
235
|
extra_arg := next(
|
|
@@ -238,9 +237,10 @@ class EventHandler(EventActionsMixin):
|
|
|
238
237
|
),
|
|
239
238
|
Unset,
|
|
240
239
|
):
|
|
241
|
-
|
|
240
|
+
msg = (
|
|
242
241
|
f"Event handler {self.fn.__name__} received extra argument {extra_arg}."
|
|
243
242
|
)
|
|
243
|
+
raise EventHandlerTypeError(msg)
|
|
244
244
|
|
|
245
245
|
fn_args = fn_args[: len(args)] + list(kwargs)
|
|
246
246
|
|
|
@@ -257,9 +257,8 @@ class EventHandler(EventActionsMixin):
|
|
|
257
257
|
try:
|
|
258
258
|
values.append(LiteralVar.create(arg))
|
|
259
259
|
except TypeError as e:
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
) from e
|
|
260
|
+
msg = f"Arguments to event handlers must be Vars or JSON-serializable. Got {arg} of type {type(arg)}."
|
|
261
|
+
raise EventHandlerTypeError(msg) from e
|
|
263
262
|
payload = tuple(zip(fn_args, values, strict=False))
|
|
264
263
|
|
|
265
264
|
# Return the event spec.
|
|
@@ -353,9 +352,8 @@ class EventSpec(EventActionsMixin):
|
|
|
353
352
|
for arg in args:
|
|
354
353
|
values.append(LiteralVar.create(value=arg)) # noqa: PERF401, RUF100
|
|
355
354
|
except TypeError as e:
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
) from e
|
|
355
|
+
msg = f"Arguments to event handlers must be Vars or JSON-serializable. Got {arg} of type {type(arg)}."
|
|
356
|
+
raise EventHandlerTypeError(msg) from e
|
|
359
357
|
new_payload = tuple(zip(fn_args, values, strict=False))
|
|
360
358
|
return self.with_args(self.args + new_payload)
|
|
361
359
|
|
|
@@ -408,7 +406,8 @@ class CallableEventSpec(EventSpec):
|
|
|
408
406
|
from reflex.utils.exceptions import EventHandlerTypeError
|
|
409
407
|
|
|
410
408
|
if self.fn is None:
|
|
411
|
-
|
|
409
|
+
msg = "CallableEventSpec has no associated function."
|
|
410
|
+
raise EventHandlerTypeError(msg)
|
|
412
411
|
return self.fn(*args, **kwargs)
|
|
413
412
|
|
|
414
413
|
|
|
@@ -453,7 +452,7 @@ class EventChain(EventActionsMixin):
|
|
|
453
452
|
if isinstance(value, Var):
|
|
454
453
|
if isinstance(value, EventChainVar):
|
|
455
454
|
return value
|
|
456
|
-
|
|
455
|
+
if isinstance(value, EventVar):
|
|
457
456
|
value = [value]
|
|
458
457
|
elif safe_issubclass(value._var_type, (EventChain, EventSpec)):
|
|
459
458
|
return cls.create(
|
|
@@ -463,9 +462,8 @@ class EventChain(EventActionsMixin):
|
|
|
463
462
|
**event_chain_kwargs,
|
|
464
463
|
)
|
|
465
464
|
else:
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
)
|
|
465
|
+
msg = f"Invalid event chain: {value!s} of type {value._var_type}"
|
|
466
|
+
raise ValueError(msg)
|
|
469
467
|
elif isinstance(value, EventChain):
|
|
470
468
|
# Trust that the caller knows what they're doing passing an EventChain directly
|
|
471
469
|
return value
|
|
@@ -485,15 +483,17 @@ class EventChain(EventActionsMixin):
|
|
|
485
483
|
# Call the lambda to get the event chain.
|
|
486
484
|
result = call_event_fn(v, args_spec, key=key)
|
|
487
485
|
if isinstance(result, Var):
|
|
488
|
-
|
|
486
|
+
msg = (
|
|
489
487
|
f"Invalid event chain: {v}. Cannot use a Var-returning "
|
|
490
488
|
"lambda inside an EventChain list."
|
|
491
489
|
)
|
|
490
|
+
raise ValueError(msg)
|
|
492
491
|
events.extend(result)
|
|
493
492
|
elif isinstance(v, EventVar):
|
|
494
493
|
events.append(v)
|
|
495
494
|
else:
|
|
496
|
-
|
|
495
|
+
msg = f"Invalid event: {v}"
|
|
496
|
+
raise ValueError(msg)
|
|
497
497
|
|
|
498
498
|
# If the input is a callable, create an event chain.
|
|
499
499
|
elif isinstance(value, Callable):
|
|
@@ -507,7 +507,8 @@ class EventChain(EventActionsMixin):
|
|
|
507
507
|
|
|
508
508
|
# Otherwise, raise an error.
|
|
509
509
|
else:
|
|
510
|
-
|
|
510
|
+
msg = f"Invalid event chain: {value}"
|
|
511
|
+
raise ValueError(msg)
|
|
511
512
|
|
|
512
513
|
# Add args to the event specs if necessary.
|
|
513
514
|
events = [
|
|
@@ -783,9 +784,11 @@ class FileUpload:
|
|
|
783
784
|
on_upload_progress, self.on_upload_progress_args_spec
|
|
784
785
|
)
|
|
785
786
|
else:
|
|
786
|
-
|
|
787
|
+
msg = f"{on_upload_progress} is not a valid event handler."
|
|
788
|
+
raise ValueError(msg)
|
|
787
789
|
if isinstance(events, Var):
|
|
788
|
-
|
|
790
|
+
msg = f"{on_upload_progress} cannot return a var {events}."
|
|
791
|
+
raise ValueError(msg)
|
|
789
792
|
on_upload_progress_chain = EventChain(
|
|
790
793
|
events=[*events],
|
|
791
794
|
args_spec=self.on_upload_progress_args_spec,
|
|
@@ -1081,7 +1084,8 @@ def download(
|
|
|
1081
1084
|
|
|
1082
1085
|
if isinstance(url, str):
|
|
1083
1086
|
if not url.startswith("/"):
|
|
1084
|
-
|
|
1087
|
+
msg = "The URL argument should start with a /"
|
|
1088
|
+
raise ValueError(msg)
|
|
1085
1089
|
|
|
1086
1090
|
# if filename is not provided, infer it from url
|
|
1087
1091
|
if filename is None:
|
|
@@ -1092,7 +1096,8 @@ def download(
|
|
|
1092
1096
|
|
|
1093
1097
|
if data is not None:
|
|
1094
1098
|
if url is not None:
|
|
1095
|
-
|
|
1099
|
+
msg = "Cannot provide both URL and data to download."
|
|
1100
|
+
raise ValueError(msg)
|
|
1096
1101
|
|
|
1097
1102
|
if isinstance(data, str):
|
|
1098
1103
|
# Caller provided a plain text string to download.
|
|
@@ -1115,9 +1120,8 @@ def download(
|
|
|
1115
1120
|
b64_data = b64encode(data).decode("utf-8")
|
|
1116
1121
|
url = "data:application/octet-stream;base64," + b64_data
|
|
1117
1122
|
else:
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
)
|
|
1123
|
+
msg = f"Invalid data type {type(data)} for download. Use `str` or `bytes`."
|
|
1124
|
+
raise ValueError(msg)
|
|
1121
1125
|
|
|
1122
1126
|
return server_side(
|
|
1123
1127
|
"_download",
|
|
@@ -1323,23 +1327,21 @@ def _check_event_args_subclass_of_callback(
|
|
|
1323
1327
|
except TypeError as te:
|
|
1324
1328
|
callback_name_context = f" of {callback_name}" if callback_name else ""
|
|
1325
1329
|
key_context = f" for {key}" if key else ""
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
) from te
|
|
1330
|
+
msg = f"Could not compare types {args_types_without_vars[i]} and {callback_param_name_to_type[arg]} for argument {arg}{callback_name_context}{key_context}."
|
|
1331
|
+
raise TypeError(msg) from te
|
|
1329
1332
|
|
|
1330
1333
|
if compare_result:
|
|
1331
1334
|
type_match_found[arg] = True
|
|
1332
1335
|
continue
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
f"Event handler {key} expects {args_types_without_vars[i]} for argument {arg} but got {callback_param_name_to_type[arg]}{as_annotated_in} instead."
|
|
1341
|
-
)
|
|
1336
|
+
type_match_found[arg] = False
|
|
1337
|
+
as_annotated_in = (
|
|
1338
|
+
f" as annotated in {callback_name}" if callback_name else ""
|
|
1339
|
+
)
|
|
1340
|
+
delayed_exceptions.append(
|
|
1341
|
+
EventHandlerArgTypeMismatchError(
|
|
1342
|
+
f"Event handler {key} expects {args_types_without_vars[i]} for argument {arg} but got {callback_param_name_to_type[arg]}{as_annotated_in} instead."
|
|
1342
1343
|
)
|
|
1344
|
+
)
|
|
1343
1345
|
|
|
1344
1346
|
if all(type_match_found.values()):
|
|
1345
1347
|
delayed_exceptions.clear()
|
|
@@ -1495,8 +1497,7 @@ def resolve_annotation(annotations: dict[str, Any], arg_name: str, spec: ArgsSpe
|
|
|
1495
1497
|
if annotation is None:
|
|
1496
1498
|
if not isinstance(spec, types.LambdaType):
|
|
1497
1499
|
raise MissingAnnotationError(var_name=arg_name)
|
|
1498
|
-
|
|
1499
|
-
return dict[str, dict]
|
|
1500
|
+
return dict[str, dict]
|
|
1500
1501
|
return annotation
|
|
1501
1502
|
|
|
1502
1503
|
|
|
@@ -1570,12 +1571,13 @@ def check_fn_match_arg_spec(
|
|
|
1570
1571
|
number_of_event_args = len(parsed_event_args)
|
|
1571
1572
|
|
|
1572
1573
|
if number_of_user_args - number_of_user_default_args > number_of_event_args:
|
|
1573
|
-
|
|
1574
|
+
msg = (
|
|
1574
1575
|
f"Event {key} only provides {number_of_event_args} arguments, but "
|
|
1575
1576
|
f"{func_name or user_func} requires at least {number_of_user_args - number_of_user_default_args} "
|
|
1576
1577
|
"arguments to be passed to the event handler.\n"
|
|
1577
1578
|
"See https://reflex.dev/docs/events/event-arguments/"
|
|
1578
1579
|
)
|
|
1580
|
+
raise EventFnArgMismatchError(msg)
|
|
1579
1581
|
|
|
1580
1582
|
|
|
1581
1583
|
def call_event_fn(
|
|
@@ -1630,9 +1632,8 @@ def call_event_fn(
|
|
|
1630
1632
|
|
|
1631
1633
|
# Make sure the event spec is valid.
|
|
1632
1634
|
if not isinstance(e, EventSpec):
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
)
|
|
1635
|
+
msg = f"Lambda {fn} returned an invalid event spec: {e}."
|
|
1636
|
+
raise EventHandlerValueError(msg)
|
|
1636
1637
|
|
|
1637
1638
|
# Add the event spec to the chain.
|
|
1638
1639
|
events.append(e)
|
|
@@ -1696,7 +1697,8 @@ def fix_events(
|
|
|
1696
1697
|
if isinstance(e, EventHandler):
|
|
1697
1698
|
e = e()
|
|
1698
1699
|
if not isinstance(e, EventSpec):
|
|
1699
|
-
|
|
1700
|
+
msg = f"Unexpected event type, {type(e)}."
|
|
1701
|
+
raise ValueError(msg)
|
|
1700
1702
|
name = format.format_event_handler(e.handler)
|
|
1701
1703
|
payload = {k._js_expr: v._decode() for k, v in e.args}
|
|
1702
1704
|
|
|
@@ -1749,9 +1751,8 @@ class EventVar(ObjectVar, python_types=(EventSpec, EventHandler)):
|
|
|
1749
1751
|
Raises:
|
|
1750
1752
|
TypeError: EventVar cannot be converted to a boolean.
|
|
1751
1753
|
"""
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
)
|
|
1754
|
+
msg = f"Cannot convert {self._js_expr} of type {type(self).__name__} to bool."
|
|
1755
|
+
raise TypeError(msg)
|
|
1755
1756
|
|
|
1756
1757
|
|
|
1757
1758
|
@dataclasses.dataclass(
|
|
@@ -1798,9 +1799,8 @@ class LiteralEventVar(VarOperationCall, LiteralVar, EventVar):
|
|
|
1798
1799
|
try:
|
|
1799
1800
|
value = call_event_handler(value, no_args)
|
|
1800
1801
|
except EventFnArgMismatchError:
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
) from None
|
|
1802
|
+
msg = f"Event handler {value.fn.__qualname__} used inside of a rx.cond() must not take any arguments."
|
|
1803
|
+
raise EventFnArgMismatchError(msg) from None
|
|
1804
1804
|
|
|
1805
1805
|
return cls(
|
|
1806
1806
|
_js_expr="",
|
|
@@ -1835,9 +1835,8 @@ class EventChainVar(BuilderFunctionVar, python_types=EventChain):
|
|
|
1835
1835
|
Raises:
|
|
1836
1836
|
TypeError: EventChainVar cannot be converted to a boolean.
|
|
1837
1837
|
"""
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
)
|
|
1838
|
+
msg = f"Cannot convert {self._js_expr} of type {type(self).__name__} to bool."
|
|
1839
|
+
raise TypeError(msg)
|
|
1841
1840
|
|
|
1842
1841
|
|
|
1843
1842
|
@dataclasses.dataclass(
|
|
@@ -1906,9 +1905,8 @@ class LiteralEventChainVar(ArgsFunctionOperationBuilder, LiteralVar, EventChainV
|
|
|
1906
1905
|
invocation = value.invocation
|
|
1907
1906
|
|
|
1908
1907
|
if invocation is not None and not isinstance(invocation, FunctionVar):
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
)
|
|
1908
|
+
msg = f"EventChain invocation must be a FunctionVar, got {invocation!s} of type {invocation._var_type!s}."
|
|
1909
|
+
raise ValueError(msg)
|
|
1912
1910
|
|
|
1913
1911
|
return cls(
|
|
1914
1912
|
_js_expr="",
|
|
@@ -2142,12 +2140,12 @@ class EventNamespace:
|
|
|
2142
2140
|
if not inspect.iscoroutinefunction(
|
|
2143
2141
|
func
|
|
2144
2142
|
) and not inspect.isasyncgenfunction(func):
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
)
|
|
2143
|
+
msg = "Background task must be async function or generator."
|
|
2144
|
+
raise TypeError(msg)
|
|
2148
2145
|
setattr(func, BACKGROUND_TASK_MARKER, True)
|
|
2149
2146
|
if getattr(func, "__name__", "").startswith("_"):
|
|
2150
|
-
|
|
2147
|
+
msg = "Event handlers cannot be private."
|
|
2148
|
+
raise ValueError(msg)
|
|
2151
2149
|
|
|
2152
2150
|
qualname: str | None = getattr(func, "__qualname__", None)
|
|
2153
2151
|
|
reflex/experimental/__init__.py
CHANGED
|
@@ -6,9 +6,9 @@ from reflex.components.datadisplay.shiki_code_block import code_block as code_bl
|
|
|
6
6
|
from reflex.components.props import PropsBase
|
|
7
7
|
from reflex.components.radix.themes.components.progress import progress as progress
|
|
8
8
|
from reflex.components.sonner.toast import toast as toast
|
|
9
|
+
from reflex.utils.console import warn
|
|
10
|
+
from reflex.utils.misc import run_in_thread
|
|
9
11
|
|
|
10
|
-
from ..utils.console import warn
|
|
11
|
-
from ..utils.misc import run_in_thread
|
|
12
12
|
from . import hooks as hooks
|
|
13
13
|
from .client_state import ClientStateVar as ClientStateVar
|
|
14
14
|
from .layout import layout as layout
|
|
@@ -111,7 +111,8 @@ class ClientStateVar(Var):
|
|
|
111
111
|
var_name = get_unique_variable_name()
|
|
112
112
|
id_name = "id_" + get_unique_variable_name()
|
|
113
113
|
if not isinstance(var_name, str):
|
|
114
|
-
|
|
114
|
+
msg = "var_name must be a string."
|
|
115
|
+
raise ValueError(msg)
|
|
115
116
|
if default is NoValue:
|
|
116
117
|
default_var = Var(_js_expr="")
|
|
117
118
|
elif not isinstance(default, Var):
|
|
@@ -158,7 +159,9 @@ class ClientStateVar(Var):
|
|
|
158
159
|
hooks[f"{_client_state_ref(var_name)} ??= {var_name!s}"] = None
|
|
159
160
|
hooks[f"{_client_state_ref_dict(var_name)} ??= {{}}"] = None
|
|
160
161
|
hooks[f"{_client_state_ref_dict(setter_name)} ??= {{}}"] = None
|
|
161
|
-
hooks[
|
|
162
|
+
hooks[
|
|
163
|
+
f"{_client_state_ref_dict(var_name)}[{id_name}] = {_client_state_ref(var_name)}"
|
|
164
|
+
] = None
|
|
162
165
|
hooks[
|
|
163
166
|
f"{_client_state_ref_dict(setter_name)}[{id_name}] = {setter_name}"
|
|
164
167
|
] = None
|
|
@@ -269,7 +272,8 @@ class ClientStateVar(Var):
|
|
|
269
272
|
ValueError: If the ClientStateVar is not global.
|
|
270
273
|
"""
|
|
271
274
|
if not self._global_ref:
|
|
272
|
-
|
|
275
|
+
msg = "ClientStateVar must be global to retrieve the value."
|
|
276
|
+
raise ValueError(msg)
|
|
273
277
|
return run_script(_client_state_ref(self._getter_name), callback=callback)
|
|
274
278
|
|
|
275
279
|
def push(self, value: Any) -> EventSpec:
|
|
@@ -287,6 +291,7 @@ class ClientStateVar(Var):
|
|
|
287
291
|
ValueError: If the ClientStateVar is not global.
|
|
288
292
|
"""
|
|
289
293
|
if not self._global_ref:
|
|
290
|
-
|
|
294
|
+
msg = "ClientStateVar must be global to push the value."
|
|
295
|
+
raise ValueError(msg)
|
|
291
296
|
value = Var.create(value)
|
|
292
297
|
return run_script(f"{_client_state_ref(self._setter_name)}({value})")
|
reflex/experimental/layout.pyi
CHANGED
|
@@ -237,7 +237,6 @@ class Sidebar(Box, MemoizationLeaf):
|
|
|
237
237
|
Returns:
|
|
238
238
|
The sidebar component.
|
|
239
239
|
"""
|
|
240
|
-
...
|
|
241
240
|
|
|
242
241
|
def add_style(self) -> dict[str, Any] | None: ...
|
|
243
242
|
def add_hooks(self) -> list[Var]: ...
|
|
@@ -309,7 +308,6 @@ class DrawerSidebar(DrawerRoot):
|
|
|
309
308
|
Returns:
|
|
310
309
|
The drawer sidebar component.
|
|
311
310
|
"""
|
|
312
|
-
...
|
|
313
311
|
|
|
314
312
|
sidebar_trigger_style = {
|
|
315
313
|
"position": "fixed",
|
|
@@ -362,7 +360,6 @@ class SidebarTrigger(Fragment):
|
|
|
362
360
|
Returns:
|
|
363
361
|
The sidebar trigger component.
|
|
364
362
|
"""
|
|
365
|
-
...
|
|
366
363
|
|
|
367
364
|
class Layout(Box):
|
|
368
365
|
@overload
|
|
@@ -587,7 +584,6 @@ class Layout(Box):
|
|
|
587
584
|
Returns:
|
|
588
585
|
The layout component.
|
|
589
586
|
"""
|
|
590
|
-
...
|
|
591
587
|
|
|
592
588
|
class LayoutNamespace(ComponentNamespace):
|
|
593
589
|
drawer_sidebar = staticmethod(DrawerSidebar.create)
|
|
@@ -814,6 +810,5 @@ class LayoutNamespace(ComponentNamespace):
|
|
|
814
810
|
Returns:
|
|
815
811
|
The layout component.
|
|
816
812
|
"""
|
|
817
|
-
...
|
|
818
813
|
|
|
819
814
|
layout = LayoutNamespace()
|
reflex/istate/manager.py
CHANGED
|
@@ -17,7 +17,8 @@ from redis.asyncio.client import PubSub
|
|
|
17
17
|
from typing_extensions import override
|
|
18
18
|
|
|
19
19
|
from reflex import constants
|
|
20
|
-
from reflex.config import
|
|
20
|
+
from reflex.config import get_config
|
|
21
|
+
from reflex.environment import environment
|
|
21
22
|
from reflex.state import BaseState, _split_substate_key, _substate_key
|
|
22
23
|
from reflex.utils import console, path_ops, prerequisites
|
|
23
24
|
from reflex.utils.exceptions import (
|
|
@@ -66,9 +67,8 @@ class StateManager(ABC):
|
|
|
66
67
|
lock_expiration=config.redis_lock_expiration,
|
|
67
68
|
lock_warning_threshold=config.redis_lock_warning_threshold,
|
|
68
69
|
)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
)
|
|
70
|
+
msg = f"Expected one of: DISK, MEMORY, REDIS, got {config.state_manager_mode}"
|
|
71
|
+
raise InvalidStateManagerModeError(msg)
|
|
72
72
|
|
|
73
73
|
@abstractmethod
|
|
74
74
|
async def get_state(self, token: str) -> BaseState:
|
|
@@ -80,7 +80,6 @@ class StateManager(ABC):
|
|
|
80
80
|
Returns:
|
|
81
81
|
The state for the token.
|
|
82
82
|
"""
|
|
83
|
-
pass
|
|
84
83
|
|
|
85
84
|
@abstractmethod
|
|
86
85
|
async def set_state(self, token: str, state: BaseState):
|
|
@@ -90,7 +89,6 @@ class StateManager(ABC):
|
|
|
90
89
|
token: The token to set the state for.
|
|
91
90
|
state: The state to set.
|
|
92
91
|
"""
|
|
93
|
-
pass
|
|
94
92
|
|
|
95
93
|
@abstractmethod
|
|
96
94
|
@contextlib.asynccontextmanager
|
|
@@ -145,7 +143,6 @@ class StateManagerMemory(StateManager):
|
|
|
145
143
|
token: The token to set the state for.
|
|
146
144
|
state: The state to set.
|
|
147
145
|
"""
|
|
148
|
-
pass
|
|
149
146
|
|
|
150
147
|
@override
|
|
151
148
|
@contextlib.asynccontextmanager
|
|
@@ -269,6 +266,7 @@ class StateManagerDisk(StateManager):
|
|
|
269
266
|
return BaseState._deserialize(fp=file)
|
|
270
267
|
except Exception:
|
|
271
268
|
pass
|
|
269
|
+
return None
|
|
272
270
|
|
|
273
271
|
async def populate_substates(
|
|
274
272
|
self, client_token: str, state: BaseState, root_state: BaseState
|
|
@@ -449,9 +447,8 @@ class StateManagerRedis(StateManager):
|
|
|
449
447
|
InvalidLockWarningThresholdError: If the lock warning threshold is invalid.
|
|
450
448
|
"""
|
|
451
449
|
if self.lock_warning_threshold >= (lock_expiration := self.lock_expiration):
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
)
|
|
450
|
+
msg = f"The lock warning threshold({self.lock_warning_threshold}) must be less than the lock expiration time({lock_expiration})."
|
|
451
|
+
raise InvalidLockWarningThresholdError(msg)
|
|
455
452
|
|
|
456
453
|
def _get_required_state_classes(
|
|
457
454
|
self,
|
|
@@ -557,9 +554,8 @@ class StateManagerRedis(StateManager):
|
|
|
557
554
|
# Get the State class associated with the given path.
|
|
558
555
|
state_cls = self.state.get_class_substate(state_path)
|
|
559
556
|
else:
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
)
|
|
557
|
+
msg = f"StateManagerRedis requires token to be specified in the form of {{token}}_{{state_full_name}}, but got {token}"
|
|
558
|
+
raise RuntimeError(msg)
|
|
563
559
|
|
|
564
560
|
# Determine which states we already have.
|
|
565
561
|
flat_state_tree: dict[str, BaseState] = (
|
|
@@ -601,11 +597,12 @@ class StateManagerRedis(StateManager):
|
|
|
601
597
|
)
|
|
602
598
|
parent_state = flat_state_tree.get(parent_state_name)
|
|
603
599
|
if parent_state is None:
|
|
604
|
-
|
|
600
|
+
msg = (
|
|
605
601
|
f"Parent state for {state.get_full_name()} was not found "
|
|
606
602
|
"in the state tree, but should have already been fetched. "
|
|
607
|
-
"This is a bug"
|
|
603
|
+
"This is a bug"
|
|
608
604
|
)
|
|
605
|
+
raise RuntimeError(msg)
|
|
609
606
|
parent_state.substates[state_name] = state
|
|
610
607
|
state.parent_state = parent_state
|
|
611
608
|
|
|
@@ -638,12 +635,13 @@ class StateManagerRedis(StateManager):
|
|
|
638
635
|
lock_id is not None
|
|
639
636
|
and await self.redis.get(self._lock_key(token)) != lock_id
|
|
640
637
|
):
|
|
641
|
-
|
|
638
|
+
msg = (
|
|
642
639
|
f"Lock expired for token {token} while processing. Consider increasing "
|
|
643
640
|
f"`app.state_manager.lock_expiration` (currently {self.lock_expiration}) "
|
|
644
641
|
"or use `@rx.event(background=True)` decorator for long-running tasks."
|
|
645
642
|
)
|
|
646
|
-
|
|
643
|
+
raise LockExpiredError(msg)
|
|
644
|
+
if lock_id is not None:
|
|
647
645
|
time_taken = self.lock_expiration / 1000 - (
|
|
648
646
|
await self.redis.ttl(self._lock_key(token))
|
|
649
647
|
)
|
|
@@ -657,9 +655,8 @@ class StateManagerRedis(StateManager):
|
|
|
657
655
|
client_token, substate_name = _split_substate_key(token)
|
|
658
656
|
# If the substate name on the token doesn't match the instance name, it cannot have a parent.
|
|
659
657
|
if state.parent_state is not None and state.get_full_name() != substate_name:
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
)
|
|
658
|
+
msg = f"Cannot `set_state` with mismatching token {token} and substate {state.get_full_name()}."
|
|
659
|
+
raise RuntimeError(msg)
|
|
663
660
|
|
|
664
661
|
# Recursively set_state on all known substates.
|
|
665
662
|
tasks = [
|
reflex/istate/proxy.py
CHANGED
|
@@ -122,9 +122,8 @@ class StateProxy(wrapt.ObjectProxy):
|
|
|
122
122
|
self._self_actx_lock.locked()
|
|
123
123
|
and current_task == self._self_actx_lock_holder
|
|
124
124
|
):
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
)
|
|
125
|
+
msg = "The state is already mutable. Do not nest `async with self` blocks."
|
|
126
|
+
raise ImmutableStateError(msg)
|
|
128
127
|
|
|
129
128
|
from reflex.state import _substate_key
|
|
130
129
|
|
|
@@ -173,7 +172,8 @@ class StateProxy(wrapt.ObjectProxy):
|
|
|
173
172
|
Raises:
|
|
174
173
|
TypeError: always, because only async contextmanager protocol is supported.
|
|
175
174
|
"""
|
|
176
|
-
|
|
175
|
+
msg = "Background task must use `async with self` to modify state."
|
|
176
|
+
raise TypeError(msg)
|
|
177
177
|
|
|
178
178
|
def __exit__(self, *exc_info: Any) -> None:
|
|
179
179
|
"""Exit the regular context manager protocol.
|
|
@@ -181,7 +181,6 @@ class StateProxy(wrapt.ObjectProxy):
|
|
|
181
181
|
Args:
|
|
182
182
|
exc_info: The exception info tuple.
|
|
183
183
|
"""
|
|
184
|
-
pass
|
|
185
184
|
|
|
186
185
|
def __getattr__(self, name: str) -> Any:
|
|
187
186
|
"""Get the attribute from the underlying state instance.
|
|
@@ -196,10 +195,11 @@ class StateProxy(wrapt.ObjectProxy):
|
|
|
196
195
|
ImmutableStateError: If the state is not in mutable mode.
|
|
197
196
|
"""
|
|
198
197
|
if name in ["substates", "parent_state"] and not self._is_mutable():
|
|
199
|
-
|
|
198
|
+
msg = (
|
|
200
199
|
"Background task StateProxy is immutable outside of a context "
|
|
201
200
|
"manager. Use `async with self` to modify state."
|
|
202
201
|
)
|
|
202
|
+
raise ImmutableStateError(msg)
|
|
203
203
|
|
|
204
204
|
value = super().__getattr__(name)
|
|
205
205
|
if not name.startswith("_self_") and isinstance(value, MutableProxy):
|
|
@@ -243,10 +243,11 @@ class StateProxy(wrapt.ObjectProxy):
|
|
|
243
243
|
super().__setattr__(name, value)
|
|
244
244
|
return
|
|
245
245
|
|
|
246
|
-
|
|
246
|
+
msg = (
|
|
247
247
|
"Background task StateProxy is immutable outside of a context "
|
|
248
248
|
"manager. Use `async with self` to modify state."
|
|
249
249
|
)
|
|
250
|
+
raise ImmutableStateError(msg)
|
|
250
251
|
|
|
251
252
|
def get_substate(self, path: Sequence[str]) -> BaseState:
|
|
252
253
|
"""Only allow substate access with lock held.
|
|
@@ -261,10 +262,11 @@ class StateProxy(wrapt.ObjectProxy):
|
|
|
261
262
|
ImmutableStateError: If the state is not in mutable mode.
|
|
262
263
|
"""
|
|
263
264
|
if not self._is_mutable():
|
|
264
|
-
|
|
265
|
+
msg = (
|
|
265
266
|
"Background task StateProxy is immutable outside of a context "
|
|
266
267
|
"manager. Use `async with self` to modify state."
|
|
267
268
|
)
|
|
269
|
+
raise ImmutableStateError(msg)
|
|
268
270
|
return self.__wrapped__.get_substate(path)
|
|
269
271
|
|
|
270
272
|
async def get_state(self, state_cls: type[BaseState]) -> BaseState:
|
|
@@ -280,10 +282,11 @@ class StateProxy(wrapt.ObjectProxy):
|
|
|
280
282
|
ImmutableStateError: If the state is not in mutable mode.
|
|
281
283
|
"""
|
|
282
284
|
if not self._is_mutable():
|
|
283
|
-
|
|
285
|
+
msg = (
|
|
284
286
|
"Background task StateProxy is immutable outside of a context "
|
|
285
287
|
"manager. Use `async with self` to modify state."
|
|
286
288
|
)
|
|
289
|
+
raise ImmutableStateError(msg)
|
|
287
290
|
return type(self)(
|
|
288
291
|
await self.__wrapped__.get_state(state_cls), parent_state_proxy=self
|
|
289
292
|
)
|
|
@@ -323,7 +326,8 @@ class ReadOnlyStateProxy(StateProxy):
|
|
|
323
326
|
# Special case attributes of the proxy itself, not applied to the wrapped object.
|
|
324
327
|
super().__setattr__(name, value)
|
|
325
328
|
return
|
|
326
|
-
|
|
329
|
+
msg = "This is a read-only state proxy."
|
|
330
|
+
raise NotImplementedError(msg)
|
|
327
331
|
|
|
328
332
|
def mark_dirty(self):
|
|
329
333
|
"""Mark the state as dirty.
|
|
@@ -331,7 +335,8 @@ class ReadOnlyStateProxy(StateProxy):
|
|
|
331
335
|
Raises:
|
|
332
336
|
NotImplementedError: Always raised when trying to mark the proxied state as dirty.
|
|
333
337
|
"""
|
|
334
|
-
|
|
338
|
+
msg = "This is a read-only state proxy."
|
|
339
|
+
raise NotImplementedError(msg)
|
|
335
340
|
|
|
336
341
|
|
|
337
342
|
class MutableProxy(wrapt.ObjectProxy):
|
|
@@ -460,6 +465,7 @@ class MutableProxy(wrapt.ObjectProxy):
|
|
|
460
465
|
self._self_state._mark_dirty()
|
|
461
466
|
if wrapped is not None:
|
|
462
467
|
return wrapped(*args, **(kwargs or {}))
|
|
468
|
+
return None
|
|
463
469
|
|
|
464
470
|
@classmethod
|
|
465
471
|
def _is_mutable_type(cls, value: Any) -> bool:
|
|
@@ -748,10 +754,11 @@ class ImmutableMutableProxy(MutableProxy):
|
|
|
748
754
|
ImmutableStateError: if the StateProxy is not mutable.
|
|
749
755
|
"""
|
|
750
756
|
if not self._self_state._is_mutable():
|
|
751
|
-
|
|
757
|
+
msg = (
|
|
752
758
|
"Background task StateProxy is immutable outside of a context "
|
|
753
759
|
"manager. Use `async with self` to modify state."
|
|
754
760
|
)
|
|
761
|
+
raise ImmutableStateError(msg)
|
|
755
762
|
return super()._mark_dirty(
|
|
756
763
|
wrapped=wrapped, instance=instance, args=args, kwargs=kwargs
|
|
757
764
|
)
|
reflex/model.py
CHANGED
|
@@ -21,7 +21,8 @@ import sqlalchemy.orm
|
|
|
21
21
|
from alembic.runtime.migration import MigrationContext
|
|
22
22
|
|
|
23
23
|
from reflex.base import Base
|
|
24
|
-
from reflex.config import
|
|
24
|
+
from reflex.config import get_config
|
|
25
|
+
from reflex.environment import environment
|
|
25
26
|
from reflex.utils import console
|
|
26
27
|
from reflex.utils.compat import sqlmodel, sqlmodel_field_has_primary_key
|
|
27
28
|
|
|
@@ -83,7 +84,8 @@ def get_engine(url: str | None = None) -> sqlalchemy.engine.Engine:
|
|
|
83
84
|
conf = get_config()
|
|
84
85
|
url = url or conf.db_url
|
|
85
86
|
if url is None:
|
|
86
|
-
|
|
87
|
+
msg = "No database url configured"
|
|
88
|
+
raise ValueError(msg)
|
|
87
89
|
|
|
88
90
|
global _ENGINE
|
|
89
91
|
if url in _ENGINE:
|
|
@@ -125,7 +127,8 @@ def get_async_engine(url: str | None) -> sqlalchemy.ext.asyncio.AsyncEngine:
|
|
|
125
127
|
f"db_url `{_safe_db_url_for_logging(conf.db_url)}`."
|
|
126
128
|
)
|
|
127
129
|
if url is None:
|
|
128
|
-
|
|
130
|
+
msg = "No async database url configured"
|
|
131
|
+
raise ValueError(msg)
|
|
129
132
|
|
|
130
133
|
global _ASYNC_ENGINE
|
|
131
134
|
if url in _ASYNC_ENGINE:
|
|
@@ -271,7 +274,7 @@ class Model(Base, sqlmodel.SQLModel): # pyright: ignore [reportGeneralTypeIssue
|
|
|
271
274
|
"""
|
|
272
275
|
if hasattr(value, "dict"):
|
|
273
276
|
return value.dict()
|
|
274
|
-
|
|
277
|
+
if isinstance(value, list):
|
|
275
278
|
return [cls._dict_recursive(item) for item in value]
|
|
276
279
|
return value
|
|
277
280
|
|
|
@@ -481,7 +484,7 @@ class Model(Base, sqlmodel.SQLModel): # pyright: ignore [reportGeneralTypeIssue
|
|
|
481
484
|
None - indicating the process was skipped.
|
|
482
485
|
"""
|
|
483
486
|
if not environment.ALEMBIC_CONFIG.get().exists():
|
|
484
|
-
return
|
|
487
|
+
return None
|
|
485
488
|
|
|
486
489
|
with cls.get_db_engine().connect() as connection:
|
|
487
490
|
cls._alembic_upgrade(connection=connection)
|