reflex 0.6.4a3__py3-none-any.whl → 0.6.5a1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- reflex/.templates/jinja/web/pages/custom_component.js.jinja2 +0 -14
- reflex/.templates/jinja/web/pages/utils.js.jinja2 +4 -8
- reflex/.templates/web/components/shiki/code.js +16 -11
- reflex/.templates/web/utils/state.js +29 -21
- reflex/__init__.py +4 -0
- reflex/__init__.pyi +4 -0
- reflex/app.py +148 -154
- reflex/app_mixins/lifespan.py +5 -1
- reflex/app_mixins/middleware.py +3 -1
- reflex/app_mixins/mixin.py +3 -2
- reflex/base.py +2 -4
- reflex/compiler/compiler.py +111 -37
- reflex/components/base/app_wrap.pyi +17 -17
- reflex/components/base/bare.py +72 -3
- reflex/components/base/body.pyi +17 -17
- reflex/components/base/document.pyi +81 -81
- reflex/components/base/error_boundary.pyi +25 -18
- reflex/components/base/fragment.pyi +17 -17
- reflex/components/base/head.pyi +33 -33
- reflex/components/base/link.pyi +33 -33
- reflex/components/base/meta.pyi +65 -65
- reflex/components/base/script.py +4 -4
- reflex/components/base/script.pyi +23 -20
- reflex/components/component.py +250 -31
- reflex/components/core/banner.py +1 -1
- reflex/components/core/banner.pyi +81 -81
- reflex/components/core/client_side_routing.pyi +33 -33
- reflex/components/core/clipboard.py +2 -2
- reflex/components/core/clipboard.pyi +24 -18
- reflex/components/core/debounce.py +2 -2
- reflex/components/core/debounce.pyi +18 -18
- reflex/components/core/html.pyi +17 -17
- reflex/components/core/upload.py +82 -28
- reflex/components/core/upload.pyi +77 -72
- reflex/components/datadisplay/code.py +55 -40
- reflex/components/datadisplay/code.pyi +46 -44
- reflex/components/datadisplay/dataeditor.py +21 -20
- reflex/components/datadisplay/dataeditor.pyi +103 -35
- reflex/components/datadisplay/shiki_code_block.py +60 -27
- reflex/components/datadisplay/shiki_code_block.pyi +86 -65
- reflex/components/dynamic.py +9 -5
- reflex/components/el/element.pyi +17 -17
- reflex/components/el/elements/base.pyi +17 -17
- reflex/components/el/elements/forms.py +12 -3
- reflex/components/el/elements/forms.pyi +293 -233
- reflex/components/el/elements/inline.pyi +449 -449
- reflex/components/el/elements/media.pyi +401 -401
- reflex/components/el/elements/metadata.pyi +97 -97
- reflex/components/el/elements/other.pyi +113 -113
- reflex/components/el/elements/scripts.pyi +49 -49
- reflex/components/el/elements/sectioning.pyi +241 -241
- reflex/components/el/elements/tables.pyi +161 -161
- reflex/components/el/elements/typography.pyi +241 -241
- reflex/components/gridjs/datatable.pyi +33 -33
- reflex/components/lucide/icon.py +1 -1
- reflex/components/lucide/icon.pyi +33 -33
- reflex/components/markdown/markdown.py +180 -49
- reflex/components/markdown/markdown.pyi +36 -19
- reflex/components/moment/moment.py +17 -21
- reflex/components/moment/moment.pyi +26 -21
- reflex/components/next/base.pyi +17 -17
- reflex/components/next/image.py +3 -3
- reflex/components/next/image.pyi +21 -19
- reflex/components/next/link.pyi +17 -17
- reflex/components/next/video.pyi +17 -17
- reflex/components/plotly/plotly.py +79 -78
- reflex/components/plotly/plotly.pyi +91 -41
- reflex/components/props.py +34 -0
- reflex/components/radix/primitives/accordion.py +15 -8
- reflex/components/radix/primitives/accordion.pyi +121 -118
- reflex/components/radix/primitives/base.pyi +33 -33
- reflex/components/radix/primitives/drawer.py +41 -20
- reflex/components/radix/primitives/drawer.pyi +279 -190
- reflex/components/radix/primitives/form.py +2 -2
- reflex/components/radix/primitives/form.pyi +200 -167
- reflex/components/radix/primitives/progress.pyi +81 -81
- reflex/components/radix/primitives/slider.pyi +89 -83
- reflex/components/radix/themes/base.py +27 -1
- reflex/components/radix/themes/base.pyi +286 -113
- reflex/components/radix/themes/color_mode.py +17 -9
- reflex/components/radix/themes/color_mode.pyi +68 -56
- reflex/components/radix/themes/components/alert_dialog.py +8 -5
- reflex/components/radix/themes/components/alert_dialog.pyi +125 -117
- reflex/components/radix/themes/components/aspect_ratio.pyi +17 -17
- reflex/components/radix/themes/components/avatar.py +1 -5
- reflex/components/radix/themes/components/avatar.pyi +17 -17
- reflex/components/radix/themes/components/badge.py +1 -5
- reflex/components/radix/themes/components/badge.pyi +17 -17
- reflex/components/radix/themes/components/button.pyi +18 -21
- reflex/components/radix/themes/components/callout.py +1 -4
- reflex/components/radix/themes/components/callout.pyi +81 -81
- reflex/components/radix/themes/components/card.py +1 -3
- reflex/components/radix/themes/components/card.pyi +17 -17
- reflex/components/radix/themes/components/checkbox.py +4 -8
- reflex/components/radix/themes/components/checkbox.pyi +61 -52
- reflex/components/radix/themes/components/checkbox_cards.pyi +33 -33
- reflex/components/radix/themes/components/checkbox_group.pyi +33 -33
- reflex/components/radix/themes/components/context_menu.py +121 -28
- reflex/components/radix/themes/components/context_menu.pyi +250 -147
- reflex/components/radix/themes/components/data_list.pyi +65 -65
- reflex/components/radix/themes/components/dialog.py +11 -11
- reflex/components/radix/themes/components/dialog.pyi +135 -120
- reflex/components/radix/themes/components/dropdown_menu.py +14 -25
- reflex/components/radix/themes/components/dropdown_menu.pyi +157 -145
- reflex/components/radix/themes/components/hover_card.py +19 -7
- reflex/components/radix/themes/components/hover_card.pyi +102 -67
- reflex/components/radix/themes/components/icon_button.pyi +18 -21
- reflex/components/radix/themes/components/inset.py +1 -3
- reflex/components/radix/themes/components/inset.pyi +17 -17
- reflex/components/radix/themes/components/popover.py +22 -13
- reflex/components/radix/themes/components/popover.pyi +98 -72
- reflex/components/radix/themes/components/progress.pyi +17 -17
- reflex/components/radix/themes/components/radio.pyi +17 -17
- reflex/components/radix/themes/components/radio_cards.py +2 -2
- reflex/components/radix/themes/components/radio_cards.pyi +37 -34
- reflex/components/radix/themes/components/radio_group.py +3 -7
- reflex/components/radix/themes/components/radio_group.pyi +69 -66
- reflex/components/radix/themes/components/scroll_area.py +1 -3
- reflex/components/radix/themes/components/scroll_area.pyi +17 -17
- reflex/components/radix/themes/components/segmented_control.pyi +37 -34
- reflex/components/radix/themes/components/select.py +7 -11
- reflex/components/radix/themes/components/select.pyi +175 -154
- reflex/components/radix/themes/components/separator.py +1 -4
- reflex/components/radix/themes/components/separator.pyi +17 -17
- reflex/components/radix/themes/components/skeleton.pyi +17 -17
- reflex/components/radix/themes/components/slider.py +12 -21
- reflex/components/radix/themes/components/slider.pyi +47 -25
- reflex/components/radix/themes/components/spinner.py +1 -4
- reflex/components/radix/themes/components/spinner.pyi +17 -17
- reflex/components/radix/themes/components/switch.py +3 -6
- reflex/components/radix/themes/components/switch.pyi +21 -18
- reflex/components/radix/themes/components/table.py +21 -5
- reflex/components/radix/themes/components/table.pyi +392 -116
- reflex/components/radix/themes/components/tabs.py +3 -6
- reflex/components/radix/themes/components/tabs.pyi +89 -83
- reflex/components/radix/themes/components/text_area.py +1 -5
- reflex/components/radix/themes/components/text_area.pyi +43 -20
- reflex/components/radix/themes/components/text_field.py +1 -5
- reflex/components/radix/themes/components/text_field.pyi +101 -55
- reflex/components/radix/themes/components/tooltip.py +5 -7
- reflex/components/radix/themes/components/tooltip.pyi +25 -22
- reflex/components/radix/themes/layout/base.py +2 -27
- reflex/components/radix/themes/layout/base.pyi +82 -82
- reflex/components/radix/themes/layout/box.pyi +17 -17
- reflex/components/radix/themes/layout/center.pyi +17 -17
- reflex/components/radix/themes/layout/container.pyi +17 -17
- reflex/components/radix/themes/layout/flex.py +1 -6
- reflex/components/radix/themes/layout/flex.pyi +17 -17
- reflex/components/radix/themes/layout/grid.py +1 -6
- reflex/components/radix/themes/layout/grid.pyi +17 -17
- reflex/components/radix/themes/layout/list.py +20 -15
- reflex/components/radix/themes/layout/list.pyi +175 -92
- reflex/components/radix/themes/layout/section.pyi +17 -17
- reflex/components/radix/themes/layout/spacer.pyi +17 -17
- reflex/components/radix/themes/layout/stack.py +6 -6
- reflex/components/radix/themes/layout/stack.pyi +91 -62
- reflex/components/radix/themes/typography/blockquote.py +2 -8
- reflex/components/radix/themes/typography/blockquote.pyi +17 -17
- reflex/components/radix/themes/typography/code.py +4 -10
- reflex/components/radix/themes/typography/code.pyi +19 -18
- reflex/components/radix/themes/typography/heading.py +4 -11
- reflex/components/radix/themes/typography/heading.pyi +19 -18
- reflex/components/radix/themes/typography/link.py +4 -10
- reflex/components/radix/themes/typography/link.pyi +19 -18
- reflex/components/radix/themes/typography/text.py +4 -11
- reflex/components/radix/themes/typography/text.pyi +115 -114
- reflex/components/react_player/audio.pyi +58 -33
- reflex/components/react_player/react_player.py +17 -17
- reflex/components/react_player/react_player.pyi +55 -33
- reflex/components/react_player/video.pyi +58 -33
- reflex/components/recharts/cartesian.py +45 -45
- reflex/components/recharts/cartesian.pyi +389 -304
- reflex/components/recharts/charts.py +22 -22
- reflex/components/recharts/charts.pyi +226 -179
- reflex/components/recharts/general.py +26 -27
- reflex/components/recharts/general.pyi +106 -99
- reflex/components/recharts/polar.py +33 -33
- reflex/components/recharts/polar.pyi +70 -64
- reflex/components/recharts/recharts.pyi +33 -33
- reflex/components/sonner/toast.py +9 -36
- reflex/components/sonner/toast.pyi +20 -24
- reflex/components/suneditor/editor.py +8 -8
- reflex/components/suneditor/editor.pyi +50 -25
- reflex/components/tags/iter_tag.py +1 -10
- reflex/components/tags/tag.py +1 -4
- reflex/config.py +198 -35
- reflex/constants/__init__.py +4 -16
- reflex/constants/base.py +7 -14
- reflex/constants/colors.py +0 -1
- reflex/constants/installer.py +12 -7
- reflex/constants/state.py +4 -0
- reflex/custom_components/custom_components.py +6 -6
- reflex/event.py +486 -241
- reflex/experimental/client_state.py +9 -9
- reflex/experimental/layout.py +2 -2
- reflex/experimental/layout.pyi +95 -87
- reflex/experimental/misc.py +1 -1
- reflex/istate/__init__.py +1 -0
- reflex/istate/proxy.py +33 -0
- reflex/istate/wrappers.py +27 -0
- reflex/model.py +7 -7
- reflex/page.py +2 -1
- reflex/reflex.py +142 -8
- reflex/state.py +127 -46
- reflex/testing.py +9 -7
- reflex/utils/console.py +0 -1
- reflex/utils/exceptions.py +31 -3
- reflex/utils/exec.py +33 -14
- reflex/utils/format.py +15 -12
- reflex/utils/net.py +1 -1
- reflex/utils/path_ops.py +2 -2
- reflex/utils/prerequisites.py +82 -46
- reflex/utils/pyi_generator.py +63 -20
- reflex/utils/registry.py +1 -1
- reflex/utils/serializers.py +75 -36
- reflex/utils/telemetry.py +3 -2
- reflex/utils/types.py +125 -10
- reflex/vars/base.py +131 -119
- reflex/vars/function.py +59 -12
- reflex/vars/number.py +3 -1
- reflex/vars/object.py +30 -24
- reflex/vars/sequence.py +7 -7
- {reflex-0.6.4a3.dist-info → reflex-0.6.5a1.dist-info}/METADATA +3 -3
- reflex-0.6.5a1.dist-info/RECORD +394 -0
- reflex-0.6.4a3.dist-info/RECORD +0 -391
- {reflex-0.6.4a3.dist-info → reflex-0.6.5a1.dist-info}/LICENSE +0 -0
- {reflex-0.6.4a3.dist-info → reflex-0.6.5a1.dist-info}/WHEEL +0 -0
- {reflex-0.6.4a3.dist-info → reflex-0.6.5a1.dist-info}/entry_points.txt +0 -0
reflex/reflex.py
CHANGED
|
@@ -11,6 +11,7 @@ import typer
|
|
|
11
11
|
import typer.core
|
|
12
12
|
from reflex_cli.deployments import deployments_cli
|
|
13
13
|
from reflex_cli.utils import dependency
|
|
14
|
+
from reflex_cli.v2.deployments import hosting_cli
|
|
14
15
|
|
|
15
16
|
from reflex import constants
|
|
16
17
|
from reflex.config import environment, get_config
|
|
@@ -160,7 +161,7 @@ def _run(
|
|
|
160
161
|
console.set_log_level(loglevel)
|
|
161
162
|
|
|
162
163
|
# Set env mode in the environment
|
|
163
|
-
|
|
164
|
+
environment.REFLEX_ENV_MODE.set(env)
|
|
164
165
|
|
|
165
166
|
# Show system info
|
|
166
167
|
exec.output_system_info()
|
|
@@ -277,13 +278,13 @@ def run(
|
|
|
277
278
|
False,
|
|
278
279
|
"--frontend-only",
|
|
279
280
|
help="Execute only frontend.",
|
|
280
|
-
envvar=
|
|
281
|
+
envvar=environment.REFLEX_FRONTEND_ONLY.name,
|
|
281
282
|
),
|
|
282
283
|
backend: bool = typer.Option(
|
|
283
284
|
False,
|
|
284
285
|
"--backend-only",
|
|
285
286
|
help="Execute only backend.",
|
|
286
|
-
envvar=
|
|
287
|
+
envvar=environment.REFLEX_BACKEND_ONLY.name,
|
|
287
288
|
),
|
|
288
289
|
frontend_port: str = typer.Option(
|
|
289
290
|
config.frontend_port, help="Specify a different frontend port."
|
|
@@ -302,8 +303,8 @@ def run(
|
|
|
302
303
|
if frontend and backend:
|
|
303
304
|
console.error("Cannot use both --frontend-only and --backend-only options.")
|
|
304
305
|
raise typer.Exit(1)
|
|
305
|
-
|
|
306
|
-
|
|
306
|
+
environment.REFLEX_BACKEND_ONLY.set(backend)
|
|
307
|
+
environment.REFLEX_FRONTEND_ONLY.set(frontend)
|
|
307
308
|
|
|
308
309
|
_run(env, frontend, backend, frontend_port, backend_port, backend_host, loglevel)
|
|
309
310
|
|
|
@@ -363,7 +364,7 @@ def _login() -> str:
|
|
|
363
364
|
access_token = hosting.authenticate_on_browser(invitation_code)
|
|
364
365
|
|
|
365
366
|
if not access_token:
|
|
366
|
-
console.error(
|
|
367
|
+
console.error("Unable to authenticate. Please try again or contact support.")
|
|
367
368
|
raise typer.Exit(1)
|
|
368
369
|
|
|
369
370
|
console.print("Successfully logged in.")
|
|
@@ -383,6 +384,14 @@ def login(
|
|
|
383
384
|
_login()
|
|
384
385
|
|
|
385
386
|
|
|
387
|
+
@cli.command()
|
|
388
|
+
def loginv2(loglevel: constants.LogLevel = typer.Option(config.loglevel)):
|
|
389
|
+
"""Authenicate with experimental Reflex hosting service."""
|
|
390
|
+
from reflex_cli.v2 import cli as hosting_cli
|
|
391
|
+
|
|
392
|
+
hosting_cli.login()
|
|
393
|
+
|
|
394
|
+
|
|
386
395
|
@cli.command()
|
|
387
396
|
def logout(
|
|
388
397
|
loglevel: constants.LogLevel = typer.Option(
|
|
@@ -399,13 +408,29 @@ def logout(
|
|
|
399
408
|
hosting.delete_token_from_config(include_invitation_code=True)
|
|
400
409
|
|
|
401
410
|
|
|
411
|
+
@cli.command()
|
|
412
|
+
def logoutv2(
|
|
413
|
+
loglevel: constants.LogLevel = typer.Option(
|
|
414
|
+
config.loglevel, help="The log level to use."
|
|
415
|
+
),
|
|
416
|
+
):
|
|
417
|
+
"""Log out of access to Reflex hosting service."""
|
|
418
|
+
from reflex_cli.v2.utils import hosting
|
|
419
|
+
|
|
420
|
+
console.set_log_level(loglevel)
|
|
421
|
+
|
|
422
|
+
hosting.log_out_on_browser()
|
|
423
|
+
console.debug("Deleting access token from config locally")
|
|
424
|
+
hosting.delete_token_from_config(include_invitation_code=True)
|
|
425
|
+
|
|
426
|
+
|
|
402
427
|
db_cli = typer.Typer()
|
|
403
428
|
script_cli = typer.Typer()
|
|
404
429
|
|
|
405
430
|
|
|
406
431
|
def _skip_compile():
|
|
407
432
|
"""Skip the compile step."""
|
|
408
|
-
|
|
433
|
+
environment.REFLEX_SKIP_COMPILE.set(True)
|
|
409
434
|
|
|
410
435
|
|
|
411
436
|
@db_cli.command(name="init")
|
|
@@ -420,7 +445,7 @@ def db_init():
|
|
|
420
445
|
return
|
|
421
446
|
|
|
422
447
|
# Check the alembic config.
|
|
423
|
-
if environment.ALEMBIC_CONFIG.exists():
|
|
448
|
+
if environment.ALEMBIC_CONFIG.get().exists():
|
|
424
449
|
console.error(
|
|
425
450
|
"Database is already initialized. Use "
|
|
426
451
|
"[bold]reflex db makemigrations[/bold] to create schema change "
|
|
@@ -599,6 +624,110 @@ def deploy(
|
|
|
599
624
|
)
|
|
600
625
|
|
|
601
626
|
|
|
627
|
+
@cli.command()
|
|
628
|
+
def deployv2(
|
|
629
|
+
app_name: str = typer.Option(
|
|
630
|
+
config.app_name,
|
|
631
|
+
"--app-name",
|
|
632
|
+
help="The name of the App to deploy under.",
|
|
633
|
+
hidden=True,
|
|
634
|
+
),
|
|
635
|
+
regions: List[str] = typer.Option(
|
|
636
|
+
list(),
|
|
637
|
+
"-r",
|
|
638
|
+
"--region",
|
|
639
|
+
help="The regions to deploy to. For multiple envs, repeat this option, e.g. --region sjc --region iad",
|
|
640
|
+
),
|
|
641
|
+
envs: List[str] = typer.Option(
|
|
642
|
+
list(),
|
|
643
|
+
"--env",
|
|
644
|
+
help="The environment variables to set: <key>=<value>. For multiple envs, repeat this option, e.g. --env k1=v2 --env k2=v2.",
|
|
645
|
+
),
|
|
646
|
+
vmtype: Optional[str] = typer.Option(
|
|
647
|
+
None,
|
|
648
|
+
"--vmtype",
|
|
649
|
+
help="Vm type id. Run reflex apps vmtypes list to get options.",
|
|
650
|
+
),
|
|
651
|
+
hostname: Optional[str] = typer.Option(
|
|
652
|
+
None,
|
|
653
|
+
"--hostname",
|
|
654
|
+
help="The hostname of the frontend.",
|
|
655
|
+
hidden=True,
|
|
656
|
+
),
|
|
657
|
+
interactive: bool = typer.Option(
|
|
658
|
+
True,
|
|
659
|
+
help="Whether to list configuration options and ask for confirmation.",
|
|
660
|
+
),
|
|
661
|
+
envfile: Optional[str] = typer.Option(
|
|
662
|
+
None,
|
|
663
|
+
"--envfile",
|
|
664
|
+
help="The path to an env file to use. Will override any envs set manually.",
|
|
665
|
+
hidden=True,
|
|
666
|
+
),
|
|
667
|
+
loglevel: constants.LogLevel = typer.Option(
|
|
668
|
+
config.loglevel, help="The log level to use."
|
|
669
|
+
),
|
|
670
|
+
project: Optional[str] = typer.Option(
|
|
671
|
+
None,
|
|
672
|
+
"--project",
|
|
673
|
+
help="project to deploy to",
|
|
674
|
+
hidden=True,
|
|
675
|
+
),
|
|
676
|
+
token: Optional[str] = typer.Option(
|
|
677
|
+
None,
|
|
678
|
+
"--token",
|
|
679
|
+
help="token to use for auth",
|
|
680
|
+
hidden=True,
|
|
681
|
+
),
|
|
682
|
+
):
|
|
683
|
+
"""Deploy the app to the Reflex hosting service."""
|
|
684
|
+
from reflex_cli.v2 import cli as hosting_cli
|
|
685
|
+
from reflex_cli.v2.utils import dependency
|
|
686
|
+
|
|
687
|
+
from reflex.utils import export as export_utils
|
|
688
|
+
from reflex.utils import prerequisites
|
|
689
|
+
|
|
690
|
+
# Set the log level.
|
|
691
|
+
console.set_log_level(loglevel)
|
|
692
|
+
|
|
693
|
+
# Only check requirements if interactive.
|
|
694
|
+
# There is user interaction for requirements update.
|
|
695
|
+
if interactive:
|
|
696
|
+
dependency.check_requirements()
|
|
697
|
+
|
|
698
|
+
# Check if we are set up.
|
|
699
|
+
if prerequisites.needs_reinit(frontend=True):
|
|
700
|
+
_init(name=config.app_name, loglevel=loglevel)
|
|
701
|
+
prerequisites.check_latest_package_version(constants.ReflexHostingCLI.MODULE_NAME)
|
|
702
|
+
|
|
703
|
+
hosting_cli.deploy(
|
|
704
|
+
app_name=app_name,
|
|
705
|
+
export_fn=lambda zip_dest_dir,
|
|
706
|
+
api_url,
|
|
707
|
+
deploy_url,
|
|
708
|
+
frontend,
|
|
709
|
+
backend,
|
|
710
|
+
zipping: export_utils.export(
|
|
711
|
+
zip_dest_dir=zip_dest_dir,
|
|
712
|
+
api_url=api_url,
|
|
713
|
+
deploy_url=deploy_url,
|
|
714
|
+
frontend=frontend,
|
|
715
|
+
backend=backend,
|
|
716
|
+
zipping=zipping,
|
|
717
|
+
loglevel=loglevel.subprocess_level(),
|
|
718
|
+
),
|
|
719
|
+
regions=regions,
|
|
720
|
+
envs=envs,
|
|
721
|
+
vmtype=vmtype,
|
|
722
|
+
envfile=envfile,
|
|
723
|
+
hostname=hostname,
|
|
724
|
+
interactive=interactive,
|
|
725
|
+
loglevel=loglevel.subprocess_level(),
|
|
726
|
+
token=token,
|
|
727
|
+
project=project,
|
|
728
|
+
)
|
|
729
|
+
|
|
730
|
+
|
|
602
731
|
cli.add_typer(db_cli, name="db", help="Subcommands for managing the database schema.")
|
|
603
732
|
cli.add_typer(script_cli, name="script", help="Subcommands running helper scripts.")
|
|
604
733
|
cli.add_typer(
|
|
@@ -606,6 +735,11 @@ cli.add_typer(
|
|
|
606
735
|
name="deployments",
|
|
607
736
|
help="Subcommands for managing the Deployments.",
|
|
608
737
|
)
|
|
738
|
+
cli.add_typer(
|
|
739
|
+
hosting_cli,
|
|
740
|
+
name="apps",
|
|
741
|
+
help="Subcommands for managing the Deployments.",
|
|
742
|
+
)
|
|
609
743
|
cli.add_typer(
|
|
610
744
|
custom_components_cli,
|
|
611
745
|
name="component",
|
reflex/state.py
CHANGED
|
@@ -8,8 +8,10 @@ import copy
|
|
|
8
8
|
import dataclasses
|
|
9
9
|
import functools
|
|
10
10
|
import inspect
|
|
11
|
+
import json
|
|
11
12
|
import pickle
|
|
12
13
|
import sys
|
|
14
|
+
import typing
|
|
13
15
|
import uuid
|
|
14
16
|
from abc import ABC, abstractmethod
|
|
15
17
|
from collections import defaultdict
|
|
@@ -43,9 +45,7 @@ from typing_extensions import Self
|
|
|
43
45
|
from reflex import event
|
|
44
46
|
from reflex.config import get_config
|
|
45
47
|
from reflex.istate.data import RouterData
|
|
46
|
-
from reflex.istate.storage import
|
|
47
|
-
ClientStorageBase,
|
|
48
|
-
)
|
|
48
|
+
from reflex.istate.storage import ClientStorageBase
|
|
49
49
|
from reflex.vars.base import (
|
|
50
50
|
ComputedVar,
|
|
51
51
|
DynamicRouteVar,
|
|
@@ -91,7 +91,13 @@ from reflex.utils.exceptions import (
|
|
|
91
91
|
)
|
|
92
92
|
from reflex.utils.exec import is_testing_env
|
|
93
93
|
from reflex.utils.serializers import serializer
|
|
94
|
-
from reflex.utils.types import
|
|
94
|
+
from reflex.utils.types import (
|
|
95
|
+
_isinstance,
|
|
96
|
+
get_origin,
|
|
97
|
+
is_union,
|
|
98
|
+
override,
|
|
99
|
+
value_inside_optional,
|
|
100
|
+
)
|
|
95
101
|
from reflex.vars import VarData
|
|
96
102
|
|
|
97
103
|
if TYPE_CHECKING:
|
|
@@ -104,6 +110,17 @@ var = computed_var
|
|
|
104
110
|
|
|
105
111
|
# If the state is this large, it's considered a performance issue.
|
|
106
112
|
TOO_LARGE_SERIALIZED_STATE = 100 * 1024 # 100kb
|
|
113
|
+
# Only warn about each state class size once.
|
|
114
|
+
_WARNED_ABOUT_STATE_SIZE: Set[str] = set()
|
|
115
|
+
|
|
116
|
+
# Errors caught during pickling of state
|
|
117
|
+
HANDLED_PICKLE_ERRORS = (
|
|
118
|
+
pickle.PicklingError,
|
|
119
|
+
AttributeError,
|
|
120
|
+
IndexError,
|
|
121
|
+
TypeError,
|
|
122
|
+
ValueError,
|
|
123
|
+
)
|
|
107
124
|
|
|
108
125
|
|
|
109
126
|
def _no_chain_background_task(
|
|
@@ -344,7 +361,6 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
344
361
|
|
|
345
362
|
def __init__(
|
|
346
363
|
self,
|
|
347
|
-
*args,
|
|
348
364
|
parent_state: BaseState | None = None,
|
|
349
365
|
init_substates: bool = True,
|
|
350
366
|
_reflex_internal_init: bool = False,
|
|
@@ -355,11 +371,10 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
355
371
|
DO NOT INSTANTIATE STATE CLASSES DIRECTLY! Use StateManager.get_state() instead.
|
|
356
372
|
|
|
357
373
|
Args:
|
|
358
|
-
*args: The args to pass to the Pydantic init method.
|
|
359
374
|
parent_state: The parent state.
|
|
360
375
|
init_substates: Whether to initialize the substates in this instance.
|
|
361
376
|
_reflex_internal_init: A flag to indicate that the state is being initialized by the framework.
|
|
362
|
-
**kwargs: The kwargs to
|
|
377
|
+
**kwargs: The kwargs to set as attributes on the state.
|
|
363
378
|
|
|
364
379
|
Raises:
|
|
365
380
|
ReflexRuntimeError: If the state is instantiated directly by end user.
|
|
@@ -372,7 +387,9 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
372
387
|
"See https://reflex.dev/docs/state/ for further information."
|
|
373
388
|
)
|
|
374
389
|
kwargs["parent_state"] = parent_state
|
|
375
|
-
super().__init__(
|
|
390
|
+
super().__init__()
|
|
391
|
+
for name, value in kwargs.items():
|
|
392
|
+
setattr(self, name, value)
|
|
376
393
|
|
|
377
394
|
# Setup the substates (for memory state manager only).
|
|
378
395
|
if init_substates:
|
|
@@ -636,7 +653,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
636
653
|
def computed_var_func(state: Self):
|
|
637
654
|
result = f(state)
|
|
638
655
|
|
|
639
|
-
if not
|
|
656
|
+
if not _isinstance(result, of_type):
|
|
640
657
|
console.warn(
|
|
641
658
|
f"Inline ComputedVar {f} expected type {of_type}, got {type(result)}. "
|
|
642
659
|
"You can specify expected type with `of_type` argument."
|
|
@@ -1026,9 +1043,9 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
1026
1043
|
Args:
|
|
1027
1044
|
prop: The var to create a setter for.
|
|
1028
1045
|
"""
|
|
1029
|
-
setter_name = prop.
|
|
1046
|
+
setter_name = prop._get_setter_name(include_state=False)
|
|
1030
1047
|
if setter_name not in cls.__dict__:
|
|
1031
|
-
event_handler = cls._create_event_handler(prop.
|
|
1048
|
+
event_handler = cls._create_event_handler(prop._get_setter())
|
|
1032
1049
|
cls.event_handlers[setter_name] = event_handler
|
|
1033
1050
|
setattr(cls, setter_name, event_handler)
|
|
1034
1051
|
|
|
@@ -1042,7 +1059,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
1042
1059
|
# Get the pydantic field for the var.
|
|
1043
1060
|
field = cls.get_fields()[prop._var_field_name]
|
|
1044
1061
|
if field.required:
|
|
1045
|
-
default_value = prop.
|
|
1062
|
+
default_value = prop._get_default_value()
|
|
1046
1063
|
if default_value is not None:
|
|
1047
1064
|
field.required = False
|
|
1048
1065
|
field.default = default_value
|
|
@@ -1069,7 +1086,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
1069
1086
|
return getattr(cls, name)
|
|
1070
1087
|
except AttributeError:
|
|
1071
1088
|
try:
|
|
1072
|
-
return Var("", _var_type=annotation_value).
|
|
1089
|
+
return Var("", _var_type=annotation_value)._get_default_value()
|
|
1073
1090
|
except TypeError:
|
|
1074
1091
|
pass
|
|
1075
1092
|
return None
|
|
@@ -1274,6 +1291,22 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
1274
1291
|
f"All state variables must be declared before they can be set."
|
|
1275
1292
|
)
|
|
1276
1293
|
|
|
1294
|
+
fields = self.get_fields()
|
|
1295
|
+
|
|
1296
|
+
if name in fields:
|
|
1297
|
+
field = fields[name]
|
|
1298
|
+
field_type = field.outer_type_
|
|
1299
|
+
if field.allow_none:
|
|
1300
|
+
field_type = Union[field_type, None]
|
|
1301
|
+
if not _isinstance(value, field_type):
|
|
1302
|
+
console.deprecate(
|
|
1303
|
+
"mismatched-type-assignment",
|
|
1304
|
+
f"Tried to assign value {value} of type {type(value)} to field {type(self).__name__}.{name} of type {field_type}."
|
|
1305
|
+
" This might lead to unexpected behavior.",
|
|
1306
|
+
"0.6.5",
|
|
1307
|
+
"0.7.0",
|
|
1308
|
+
)
|
|
1309
|
+
|
|
1277
1310
|
# Set the attribute.
|
|
1278
1311
|
super().__setattr__(name, value)
|
|
1279
1312
|
|
|
@@ -1687,6 +1720,35 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
1687
1720
|
# Get the function to process the event.
|
|
1688
1721
|
fn = functools.partial(handler.fn, state)
|
|
1689
1722
|
|
|
1723
|
+
try:
|
|
1724
|
+
type_hints = typing.get_type_hints(handler.fn)
|
|
1725
|
+
except Exception:
|
|
1726
|
+
type_hints = {}
|
|
1727
|
+
|
|
1728
|
+
for arg, value in list(payload.items()):
|
|
1729
|
+
hinted_args = type_hints.get(arg, Any)
|
|
1730
|
+
if hinted_args is Any:
|
|
1731
|
+
continue
|
|
1732
|
+
if is_union(hinted_args):
|
|
1733
|
+
if value is None:
|
|
1734
|
+
continue
|
|
1735
|
+
hinted_args = value_inside_optional(hinted_args)
|
|
1736
|
+
if (
|
|
1737
|
+
isinstance(value, dict)
|
|
1738
|
+
and inspect.isclass(hinted_args)
|
|
1739
|
+
and (
|
|
1740
|
+
dataclasses.is_dataclass(hinted_args)
|
|
1741
|
+
or issubclass(hinted_args, Base)
|
|
1742
|
+
)
|
|
1743
|
+
):
|
|
1744
|
+
payload[arg] = hinted_args(**value)
|
|
1745
|
+
if isinstance(value, list) and (hinted_args is set or hinted_args is Set):
|
|
1746
|
+
payload[arg] = set(value)
|
|
1747
|
+
if isinstance(value, list) and (
|
|
1748
|
+
hinted_args is tuple or hinted_args is Tuple
|
|
1749
|
+
):
|
|
1750
|
+
payload[arg] = tuple(value)
|
|
1751
|
+
|
|
1690
1752
|
# Wrap the function in a try/except block.
|
|
1691
1753
|
try:
|
|
1692
1754
|
# Handle async functions.
|
|
@@ -2024,6 +2086,27 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
2024
2086
|
state["__dict__"].pop(inherited_var_name, None)
|
|
2025
2087
|
return state
|
|
2026
2088
|
|
|
2089
|
+
def _warn_if_too_large(
|
|
2090
|
+
self,
|
|
2091
|
+
pickle_state_size: int,
|
|
2092
|
+
):
|
|
2093
|
+
"""Print a warning when the state is too large.
|
|
2094
|
+
|
|
2095
|
+
Args:
|
|
2096
|
+
pickle_state_size: The size of the pickled state.
|
|
2097
|
+
"""
|
|
2098
|
+
state_full_name = self.get_full_name()
|
|
2099
|
+
if (
|
|
2100
|
+
state_full_name not in _WARNED_ABOUT_STATE_SIZE
|
|
2101
|
+
and pickle_state_size > TOO_LARGE_SERIALIZED_STATE
|
|
2102
|
+
and self.substates
|
|
2103
|
+
):
|
|
2104
|
+
console.warn(
|
|
2105
|
+
f"State {state_full_name} serializes to {pickle_state_size} bytes "
|
|
2106
|
+
"which may present performance issues. Consider reducing the size of this state."
|
|
2107
|
+
)
|
|
2108
|
+
_WARNED_ABOUT_STATE_SIZE.add(state_full_name)
|
|
2109
|
+
|
|
2027
2110
|
@classmethod
|
|
2028
2111
|
@functools.lru_cache()
|
|
2029
2112
|
def _to_schema(cls) -> str:
|
|
@@ -2062,8 +2145,10 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
2062
2145
|
The serialized state.
|
|
2063
2146
|
"""
|
|
2064
2147
|
try:
|
|
2065
|
-
|
|
2066
|
-
|
|
2148
|
+
pickle_state = pickle.dumps((self._to_schema(), self))
|
|
2149
|
+
self._warn_if_too_large(len(pickle_state))
|
|
2150
|
+
return pickle_state
|
|
2151
|
+
except HANDLED_PICKLE_ERRORS as og_pickle_error:
|
|
2067
2152
|
error = (
|
|
2068
2153
|
f"Failed to serialize state {self.get_full_name()} due to unpicklable object. "
|
|
2069
2154
|
"This state will not be persisted. "
|
|
@@ -2077,7 +2162,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
2077
2162
|
f"Pickle error: {og_pickle_error}. "
|
|
2078
2163
|
"Consider `pip install 'dill>=0.3.8'` for more exotic serialization support."
|
|
2079
2164
|
)
|
|
2080
|
-
except
|
|
2165
|
+
except HANDLED_PICKLE_ERRORS as ex:
|
|
2081
2166
|
error += f"Dill was also unable to pickle the state: {ex}"
|
|
2082
2167
|
console.warn(error)
|
|
2083
2168
|
return b""
|
|
@@ -2346,7 +2431,7 @@ class StateProxy(wrapt.ObjectProxy):
|
|
|
2346
2431
|
class State(rx.State):
|
|
2347
2432
|
counter: int = 0
|
|
2348
2433
|
|
|
2349
|
-
@rx.background
|
|
2434
|
+
@rx.event(background=True)
|
|
2350
2435
|
async def bg_increment(self):
|
|
2351
2436
|
await asyncio.sleep(1)
|
|
2352
2437
|
async with self:
|
|
@@ -3053,9 +3138,6 @@ class StateManagerRedis(StateManager):
|
|
|
3053
3138
|
b"evicted",
|
|
3054
3139
|
}
|
|
3055
3140
|
|
|
3056
|
-
# Only warn about each state class size once.
|
|
3057
|
-
_warned_about_state_size: ClassVar[Set[str]] = set()
|
|
3058
|
-
|
|
3059
3141
|
async def _get_parent_state(
|
|
3060
3142
|
self, token: str, state: BaseState | None = None
|
|
3061
3143
|
) -> BaseState | None:
|
|
@@ -3199,29 +3281,6 @@ class StateManagerRedis(StateManager):
|
|
|
3199
3281
|
return state._get_root_state()
|
|
3200
3282
|
return state
|
|
3201
3283
|
|
|
3202
|
-
def _warn_if_too_large(
|
|
3203
|
-
self,
|
|
3204
|
-
state: BaseState,
|
|
3205
|
-
pickle_state_size: int,
|
|
3206
|
-
):
|
|
3207
|
-
"""Print a warning when the state is too large.
|
|
3208
|
-
|
|
3209
|
-
Args:
|
|
3210
|
-
state: The state to check.
|
|
3211
|
-
pickle_state_size: The size of the pickled state.
|
|
3212
|
-
"""
|
|
3213
|
-
state_full_name = state.get_full_name()
|
|
3214
|
-
if (
|
|
3215
|
-
state_full_name not in self._warned_about_state_size
|
|
3216
|
-
and pickle_state_size > TOO_LARGE_SERIALIZED_STATE
|
|
3217
|
-
and state.substates
|
|
3218
|
-
):
|
|
3219
|
-
console.warn(
|
|
3220
|
-
f"State {state_full_name} serializes to {pickle_state_size} bytes "
|
|
3221
|
-
"which may present performance issues. Consider reducing the size of this state."
|
|
3222
|
-
)
|
|
3223
|
-
self._warned_about_state_size.add(state_full_name)
|
|
3224
|
-
|
|
3225
3284
|
@override
|
|
3226
3285
|
async def set_state(
|
|
3227
3286
|
self,
|
|
@@ -3248,7 +3307,7 @@ class StateManagerRedis(StateManager):
|
|
|
3248
3307
|
raise LockExpiredError(
|
|
3249
3308
|
f"Lock expired for token {token} while processing. Consider increasing "
|
|
3250
3309
|
f"`app.state_manager.lock_expiration` (currently {self.lock_expiration}) "
|
|
3251
|
-
"or use `@rx.background` decorator for long-running tasks."
|
|
3310
|
+
"or use `@rx.event(background=True)` decorator for long-running tasks."
|
|
3252
3311
|
)
|
|
3253
3312
|
client_token, substate_name = _split_substate_key(token)
|
|
3254
3313
|
# If the substate name on the token doesn't match the instance name, it cannot have a parent.
|
|
@@ -3272,7 +3331,6 @@ class StateManagerRedis(StateManager):
|
|
|
3272
3331
|
# Persist only the given state (parents or substates are excluded by BaseState.__getstate__).
|
|
3273
3332
|
if state._get_was_touched():
|
|
3274
3333
|
pickle_state = state._serialize()
|
|
3275
|
-
self._warn_if_too_large(state, len(pickle_state))
|
|
3276
3334
|
if pickle_state:
|
|
3277
3335
|
await self.redis.set(
|
|
3278
3336
|
_substate_key(client_token, state),
|
|
@@ -3353,7 +3411,7 @@ class StateManagerRedis(StateManager):
|
|
|
3353
3411
|
)
|
|
3354
3412
|
except ResponseError:
|
|
3355
3413
|
# Some redis servers only allow out-of-band configuration, so ignore errors here.
|
|
3356
|
-
if not environment.REFLEX_IGNORE_REDIS_CONFIG_ERROR:
|
|
3414
|
+
if not environment.REFLEX_IGNORE_REDIS_CONFIG_ERROR.get():
|
|
3357
3415
|
raise
|
|
3358
3416
|
async with self.redis.pubsub() as pubsub:
|
|
3359
3417
|
await pubsub.psubscribe(lock_key_channel)
|
|
@@ -3693,6 +3751,29 @@ def serialize_mutable_proxy(mp: MutableProxy):
|
|
|
3693
3751
|
return mp.__wrapped__
|
|
3694
3752
|
|
|
3695
3753
|
|
|
3754
|
+
_orig_json_JSONEncoder_default = json.JSONEncoder.default
|
|
3755
|
+
|
|
3756
|
+
|
|
3757
|
+
def _json_JSONEncoder_default_wrapper(self: json.JSONEncoder, o: Any) -> Any:
|
|
3758
|
+
"""Wrap JSONEncoder.default to handle MutableProxy objects.
|
|
3759
|
+
|
|
3760
|
+
Args:
|
|
3761
|
+
self: the JSONEncoder instance.
|
|
3762
|
+
o: the object to serialize.
|
|
3763
|
+
|
|
3764
|
+
Returns:
|
|
3765
|
+
A JSON-able object.
|
|
3766
|
+
"""
|
|
3767
|
+
try:
|
|
3768
|
+
return o.__wrapped__
|
|
3769
|
+
except AttributeError:
|
|
3770
|
+
pass
|
|
3771
|
+
return _orig_json_JSONEncoder_default(self, o)
|
|
3772
|
+
|
|
3773
|
+
|
|
3774
|
+
json.JSONEncoder.default = _json_JSONEncoder_default_wrapper
|
|
3775
|
+
|
|
3776
|
+
|
|
3696
3777
|
class ImmutableMutableProxy(MutableProxy):
|
|
3697
3778
|
"""A proxy for a mutable object that tracks changes.
|
|
3698
3779
|
|
reflex/testing.py
CHANGED
|
@@ -43,6 +43,7 @@ import reflex.utils.exec
|
|
|
43
43
|
import reflex.utils.format
|
|
44
44
|
import reflex.utils.prerequisites
|
|
45
45
|
import reflex.utils.processes
|
|
46
|
+
from reflex.config import environment
|
|
46
47
|
from reflex.state import (
|
|
47
48
|
BaseState,
|
|
48
49
|
StateManager,
|
|
@@ -117,7 +118,7 @@ class AppHarness:
|
|
|
117
118
|
|
|
118
119
|
app_name: str
|
|
119
120
|
app_source: Optional[
|
|
120
|
-
|
|
121
|
+
Callable[[], None] | types.ModuleType | str | functools.partial[Any]
|
|
121
122
|
]
|
|
122
123
|
app_path: pathlib.Path
|
|
123
124
|
app_module_path: pathlib.Path
|
|
@@ -137,7 +138,7 @@ class AppHarness:
|
|
|
137
138
|
cls,
|
|
138
139
|
root: pathlib.Path,
|
|
139
140
|
app_source: Optional[
|
|
140
|
-
|
|
141
|
+
Callable[[], None] | types.ModuleType | str | functools.partial[Any]
|
|
141
142
|
] = None,
|
|
142
143
|
app_name: Optional[str] = None,
|
|
143
144
|
) -> "AppHarness":
|
|
@@ -250,6 +251,7 @@ class AppHarness:
|
|
|
250
251
|
|
|
251
252
|
def _initialize_app(self):
|
|
252
253
|
# disable telemetry reporting for tests
|
|
254
|
+
|
|
253
255
|
os.environ["TELEMETRY_ENABLED"] = "false"
|
|
254
256
|
self.app_path.mkdir(parents=True, exist_ok=True)
|
|
255
257
|
if self.app_source is not None:
|
|
@@ -615,10 +617,10 @@ class AppHarness:
|
|
|
615
617
|
if self.frontend_url is None:
|
|
616
618
|
raise RuntimeError("Frontend is not running.")
|
|
617
619
|
want_headless = False
|
|
618
|
-
if
|
|
620
|
+
if environment.APP_HARNESS_HEADLESS.get():
|
|
619
621
|
want_headless = True
|
|
620
622
|
if driver_clz is None:
|
|
621
|
-
requested_driver =
|
|
623
|
+
requested_driver = environment.APP_HARNESS_DRIVER.get()
|
|
622
624
|
driver_clz = getattr(webdriver, requested_driver)
|
|
623
625
|
if driver_options is None:
|
|
624
626
|
driver_options = getattr(webdriver, f"{requested_driver}Options")()
|
|
@@ -640,7 +642,7 @@ class AppHarness:
|
|
|
640
642
|
driver_options.add_argument("headless")
|
|
641
643
|
if driver_options is None:
|
|
642
644
|
raise RuntimeError(f"Could not determine options for {driver_clz}")
|
|
643
|
-
if args :=
|
|
645
|
+
if args := environment.APP_HARNESS_DRIVER_ARGS.get():
|
|
644
646
|
for arg in args.split(","):
|
|
645
647
|
driver_options.add_argument(arg)
|
|
646
648
|
if driver_option_args is not None:
|
|
@@ -944,7 +946,7 @@ class AppHarnessProd(AppHarness):
|
|
|
944
946
|
def _start_backend(self):
|
|
945
947
|
if self.app_instance is None:
|
|
946
948
|
raise RuntimeError("App was not initialized.")
|
|
947
|
-
|
|
949
|
+
environment.REFLEX_SKIP_COMPILE.set(True)
|
|
948
950
|
self.backend = uvicorn.Server(
|
|
949
951
|
uvicorn.Config(
|
|
950
952
|
app=self.app_instance,
|
|
@@ -961,7 +963,7 @@ class AppHarnessProd(AppHarness):
|
|
|
961
963
|
try:
|
|
962
964
|
return super()._poll_for_servers(timeout)
|
|
963
965
|
finally:
|
|
964
|
-
|
|
966
|
+
environment.REFLEX_SKIP_COMPILE.set(None)
|
|
965
967
|
|
|
966
968
|
def stop(self):
|
|
967
969
|
"""Stop the frontend python webserver."""
|
reflex/utils/console.py
CHANGED
reflex/utils/exceptions.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""Custom Exceptions."""
|
|
2
2
|
|
|
3
|
+
from typing import NoReturn
|
|
4
|
+
|
|
3
5
|
|
|
4
6
|
class ReflexError(Exception):
|
|
5
7
|
"""Base exception for all Reflex exceptions."""
|
|
@@ -89,12 +91,12 @@ class MatchTypeError(ReflexError, TypeError):
|
|
|
89
91
|
"""Raised when the return types of match cases are different."""
|
|
90
92
|
|
|
91
93
|
|
|
92
|
-
class
|
|
93
|
-
"""Raised when the
|
|
94
|
+
class EventHandlerArgTypeMismatch(ReflexError, TypeError):
|
|
95
|
+
"""Raised when the annotations of args accepted by an EventHandler differs from the spec of the event trigger."""
|
|
94
96
|
|
|
95
97
|
|
|
96
98
|
class EventFnArgMismatch(ReflexError, TypeError):
|
|
97
|
-
"""Raised when the number of args
|
|
99
|
+
"""Raised when the number of args required by an event handler is more than provided by the event trigger."""
|
|
98
100
|
|
|
99
101
|
|
|
100
102
|
class DynamicRouteArgShadowsStateVar(ReflexError, NameError):
|
|
@@ -143,3 +145,29 @@ class EnvironmentVarValueError(ReflexError, ValueError):
|
|
|
143
145
|
|
|
144
146
|
class DynamicComponentInvalidSignature(ReflexError, TypeError):
|
|
145
147
|
"""Raised when a dynamic component has an invalid signature."""
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
class InvalidPropValueError(ReflexError):
|
|
151
|
+
"""Raised when a prop value is invalid."""
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class SystemPackageMissingError(ReflexError):
|
|
155
|
+
"""Raised when a system package is missing."""
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def raise_system_package_missing_error(package: str) -> NoReturn:
|
|
159
|
+
"""Raise a SystemPackageMissingError.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
package: The name of the missing system package.
|
|
163
|
+
|
|
164
|
+
Raises:
|
|
165
|
+
SystemPackageMissingError: The raised exception.
|
|
166
|
+
"""
|
|
167
|
+
from reflex.constants import IS_MACOS
|
|
168
|
+
|
|
169
|
+
raise SystemPackageMissingError(
|
|
170
|
+
f"System package '{package}' is missing."
|
|
171
|
+
" Please install it through your system package manager."
|
|
172
|
+
+ (f" You can do so by running 'brew install {package}'." if IS_MACOS else "")
|
|
173
|
+
)
|