reflex 0.6.4a2__py3-none-any.whl → 0.6.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of reflex might be problematic. Click here for more details.
- reflex/.templates/jinja/web/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 +90 -28
- reflex/components/core/upload.pyi +128 -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 +30 -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 +252 -41
- 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 +133 -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.4a2.dist-info → reflex-0.6.5.dist-info}/METADATA +3 -3
- reflex-0.6.5.dist-info/RECORD +394 -0
- reflex-0.6.4a2.dist-info/RECORD +0 -391
- {reflex-0.6.4a2.dist-info → reflex-0.6.5.dist-info}/LICENSE +0 -0
- {reflex-0.6.4a2.dist-info → reflex-0.6.5.dist-info}/WHEEL +0 -0
- {reflex-0.6.4a2.dist-info → reflex-0.6.5.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,8 @@ 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
|
-
|
|
48
|
-
)
|
|
48
|
+
from reflex.istate.storage import ClientStorageBase
|
|
49
|
+
from reflex.model import Model
|
|
49
50
|
from reflex.vars.base import (
|
|
50
51
|
ComputedVar,
|
|
51
52
|
DynamicRouteVar,
|
|
@@ -91,7 +92,13 @@ from reflex.utils.exceptions import (
|
|
|
91
92
|
)
|
|
92
93
|
from reflex.utils.exec import is_testing_env
|
|
93
94
|
from reflex.utils.serializers import serializer
|
|
94
|
-
from reflex.utils.types import
|
|
95
|
+
from reflex.utils.types import (
|
|
96
|
+
_isinstance,
|
|
97
|
+
get_origin,
|
|
98
|
+
is_union,
|
|
99
|
+
override,
|
|
100
|
+
value_inside_optional,
|
|
101
|
+
)
|
|
95
102
|
from reflex.vars import VarData
|
|
96
103
|
|
|
97
104
|
if TYPE_CHECKING:
|
|
@@ -104,6 +111,17 @@ var = computed_var
|
|
|
104
111
|
|
|
105
112
|
# If the state is this large, it's considered a performance issue.
|
|
106
113
|
TOO_LARGE_SERIALIZED_STATE = 100 * 1024 # 100kb
|
|
114
|
+
# Only warn about each state class size once.
|
|
115
|
+
_WARNED_ABOUT_STATE_SIZE: Set[str] = set()
|
|
116
|
+
|
|
117
|
+
# Errors caught during pickling of state
|
|
118
|
+
HANDLED_PICKLE_ERRORS = (
|
|
119
|
+
pickle.PicklingError,
|
|
120
|
+
AttributeError,
|
|
121
|
+
IndexError,
|
|
122
|
+
TypeError,
|
|
123
|
+
ValueError,
|
|
124
|
+
)
|
|
107
125
|
|
|
108
126
|
|
|
109
127
|
def _no_chain_background_task(
|
|
@@ -344,7 +362,6 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
344
362
|
|
|
345
363
|
def __init__(
|
|
346
364
|
self,
|
|
347
|
-
*args,
|
|
348
365
|
parent_state: BaseState | None = None,
|
|
349
366
|
init_substates: bool = True,
|
|
350
367
|
_reflex_internal_init: bool = False,
|
|
@@ -355,11 +372,10 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
355
372
|
DO NOT INSTANTIATE STATE CLASSES DIRECTLY! Use StateManager.get_state() instead.
|
|
356
373
|
|
|
357
374
|
Args:
|
|
358
|
-
*args: The args to pass to the Pydantic init method.
|
|
359
375
|
parent_state: The parent state.
|
|
360
376
|
init_substates: Whether to initialize the substates in this instance.
|
|
361
377
|
_reflex_internal_init: A flag to indicate that the state is being initialized by the framework.
|
|
362
|
-
**kwargs: The kwargs to
|
|
378
|
+
**kwargs: The kwargs to set as attributes on the state.
|
|
363
379
|
|
|
364
380
|
Raises:
|
|
365
381
|
ReflexRuntimeError: If the state is instantiated directly by end user.
|
|
@@ -372,7 +388,9 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
372
388
|
"See https://reflex.dev/docs/state/ for further information."
|
|
373
389
|
)
|
|
374
390
|
kwargs["parent_state"] = parent_state
|
|
375
|
-
super().__init__(
|
|
391
|
+
super().__init__()
|
|
392
|
+
for name, value in kwargs.items():
|
|
393
|
+
setattr(self, name, value)
|
|
376
394
|
|
|
377
395
|
# Setup the substates (for memory state manager only).
|
|
378
396
|
if init_substates:
|
|
@@ -636,7 +654,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
636
654
|
def computed_var_func(state: Self):
|
|
637
655
|
result = f(state)
|
|
638
656
|
|
|
639
|
-
if not
|
|
657
|
+
if not _isinstance(result, of_type):
|
|
640
658
|
console.warn(
|
|
641
659
|
f"Inline ComputedVar {f} expected type {of_type}, got {type(result)}. "
|
|
642
660
|
"You can specify expected type with `of_type` argument."
|
|
@@ -1026,9 +1044,9 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
1026
1044
|
Args:
|
|
1027
1045
|
prop: The var to create a setter for.
|
|
1028
1046
|
"""
|
|
1029
|
-
setter_name = prop.
|
|
1047
|
+
setter_name = prop._get_setter_name(include_state=False)
|
|
1030
1048
|
if setter_name not in cls.__dict__:
|
|
1031
|
-
event_handler = cls._create_event_handler(prop.
|
|
1049
|
+
event_handler = cls._create_event_handler(prop._get_setter())
|
|
1032
1050
|
cls.event_handlers[setter_name] = event_handler
|
|
1033
1051
|
setattr(cls, setter_name, event_handler)
|
|
1034
1052
|
|
|
@@ -1042,7 +1060,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
1042
1060
|
# Get the pydantic field for the var.
|
|
1043
1061
|
field = cls.get_fields()[prop._var_field_name]
|
|
1044
1062
|
if field.required:
|
|
1045
|
-
default_value = prop.
|
|
1063
|
+
default_value = prop._get_default_value()
|
|
1046
1064
|
if default_value is not None:
|
|
1047
1065
|
field.required = False
|
|
1048
1066
|
field.default = default_value
|
|
@@ -1069,7 +1087,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
1069
1087
|
return getattr(cls, name)
|
|
1070
1088
|
except AttributeError:
|
|
1071
1089
|
try:
|
|
1072
|
-
return Var("", _var_type=annotation_value).
|
|
1090
|
+
return Var("", _var_type=annotation_value)._get_default_value()
|
|
1073
1091
|
except TypeError:
|
|
1074
1092
|
pass
|
|
1075
1093
|
return None
|
|
@@ -1274,6 +1292,22 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
1274
1292
|
f"All state variables must be declared before they can be set."
|
|
1275
1293
|
)
|
|
1276
1294
|
|
|
1295
|
+
fields = self.get_fields()
|
|
1296
|
+
|
|
1297
|
+
if name in fields:
|
|
1298
|
+
field = fields[name]
|
|
1299
|
+
field_type = field.outer_type_
|
|
1300
|
+
if field.allow_none:
|
|
1301
|
+
field_type = Union[field_type, None]
|
|
1302
|
+
if not _isinstance(value, field_type):
|
|
1303
|
+
console.deprecate(
|
|
1304
|
+
"mismatched-type-assignment",
|
|
1305
|
+
f"Tried to assign value {value} of type {type(value)} to field {type(self).__name__}.{name} of type {field_type}."
|
|
1306
|
+
" This might lead to unexpected behavior.",
|
|
1307
|
+
"0.6.5",
|
|
1308
|
+
"0.7.0",
|
|
1309
|
+
)
|
|
1310
|
+
|
|
1277
1311
|
# Set the attribute.
|
|
1278
1312
|
super().__setattr__(name, value)
|
|
1279
1313
|
|
|
@@ -1687,6 +1721,40 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
1687
1721
|
# Get the function to process the event.
|
|
1688
1722
|
fn = functools.partial(handler.fn, state)
|
|
1689
1723
|
|
|
1724
|
+
try:
|
|
1725
|
+
type_hints = typing.get_type_hints(handler.fn)
|
|
1726
|
+
except Exception:
|
|
1727
|
+
type_hints = {}
|
|
1728
|
+
|
|
1729
|
+
for arg, value in list(payload.items()):
|
|
1730
|
+
hinted_args = type_hints.get(arg, Any)
|
|
1731
|
+
if hinted_args is Any:
|
|
1732
|
+
continue
|
|
1733
|
+
if is_union(hinted_args):
|
|
1734
|
+
if value is None:
|
|
1735
|
+
continue
|
|
1736
|
+
hinted_args = value_inside_optional(hinted_args)
|
|
1737
|
+
if isinstance(value, dict) and inspect.isclass(hinted_args):
|
|
1738
|
+
if issubclass(hinted_args, Model):
|
|
1739
|
+
# Remove non-fields from the payload
|
|
1740
|
+
payload[arg] = hinted_args(
|
|
1741
|
+
**{
|
|
1742
|
+
key: value
|
|
1743
|
+
for key, value in value.items()
|
|
1744
|
+
if key in hinted_args.__fields__
|
|
1745
|
+
}
|
|
1746
|
+
)
|
|
1747
|
+
elif dataclasses.is_dataclass(hinted_args) or issubclass(
|
|
1748
|
+
hinted_args, Base
|
|
1749
|
+
):
|
|
1750
|
+
payload[arg] = hinted_args(**value)
|
|
1751
|
+
if isinstance(value, list) and (hinted_args is set or hinted_args is Set):
|
|
1752
|
+
payload[arg] = set(value)
|
|
1753
|
+
if isinstance(value, list) and (
|
|
1754
|
+
hinted_args is tuple or hinted_args is Tuple
|
|
1755
|
+
):
|
|
1756
|
+
payload[arg] = tuple(value)
|
|
1757
|
+
|
|
1690
1758
|
# Wrap the function in a try/except block.
|
|
1691
1759
|
try:
|
|
1692
1760
|
# Handle async functions.
|
|
@@ -2024,6 +2092,27 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
2024
2092
|
state["__dict__"].pop(inherited_var_name, None)
|
|
2025
2093
|
return state
|
|
2026
2094
|
|
|
2095
|
+
def _warn_if_too_large(
|
|
2096
|
+
self,
|
|
2097
|
+
pickle_state_size: int,
|
|
2098
|
+
):
|
|
2099
|
+
"""Print a warning when the state is too large.
|
|
2100
|
+
|
|
2101
|
+
Args:
|
|
2102
|
+
pickle_state_size: The size of the pickled state.
|
|
2103
|
+
"""
|
|
2104
|
+
state_full_name = self.get_full_name()
|
|
2105
|
+
if (
|
|
2106
|
+
state_full_name not in _WARNED_ABOUT_STATE_SIZE
|
|
2107
|
+
and pickle_state_size > TOO_LARGE_SERIALIZED_STATE
|
|
2108
|
+
and self.substates
|
|
2109
|
+
):
|
|
2110
|
+
console.warn(
|
|
2111
|
+
f"State {state_full_name} serializes to {pickle_state_size} bytes "
|
|
2112
|
+
"which may present performance issues. Consider reducing the size of this state."
|
|
2113
|
+
)
|
|
2114
|
+
_WARNED_ABOUT_STATE_SIZE.add(state_full_name)
|
|
2115
|
+
|
|
2027
2116
|
@classmethod
|
|
2028
2117
|
@functools.lru_cache()
|
|
2029
2118
|
def _to_schema(cls) -> str:
|
|
@@ -2062,8 +2151,10 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
2062
2151
|
The serialized state.
|
|
2063
2152
|
"""
|
|
2064
2153
|
try:
|
|
2065
|
-
|
|
2066
|
-
|
|
2154
|
+
pickle_state = pickle.dumps((self._to_schema(), self))
|
|
2155
|
+
self._warn_if_too_large(len(pickle_state))
|
|
2156
|
+
return pickle_state
|
|
2157
|
+
except HANDLED_PICKLE_ERRORS as og_pickle_error:
|
|
2067
2158
|
error = (
|
|
2068
2159
|
f"Failed to serialize state {self.get_full_name()} due to unpicklable object. "
|
|
2069
2160
|
"This state will not be persisted. "
|
|
@@ -2077,7 +2168,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
2077
2168
|
f"Pickle error: {og_pickle_error}. "
|
|
2078
2169
|
"Consider `pip install 'dill>=0.3.8'` for more exotic serialization support."
|
|
2079
2170
|
)
|
|
2080
|
-
except
|
|
2171
|
+
except HANDLED_PICKLE_ERRORS as ex:
|
|
2081
2172
|
error += f"Dill was also unable to pickle the state: {ex}"
|
|
2082
2173
|
console.warn(error)
|
|
2083
2174
|
return b""
|
|
@@ -2346,7 +2437,7 @@ class StateProxy(wrapt.ObjectProxy):
|
|
|
2346
2437
|
class State(rx.State):
|
|
2347
2438
|
counter: int = 0
|
|
2348
2439
|
|
|
2349
|
-
@rx.background
|
|
2440
|
+
@rx.event(background=True)
|
|
2350
2441
|
async def bg_increment(self):
|
|
2351
2442
|
await asyncio.sleep(1)
|
|
2352
2443
|
async with self:
|
|
@@ -3053,9 +3144,6 @@ class StateManagerRedis(StateManager):
|
|
|
3053
3144
|
b"evicted",
|
|
3054
3145
|
}
|
|
3055
3146
|
|
|
3056
|
-
# Only warn about each state class size once.
|
|
3057
|
-
_warned_about_state_size: ClassVar[Set[str]] = set()
|
|
3058
|
-
|
|
3059
3147
|
async def _get_parent_state(
|
|
3060
3148
|
self, token: str, state: BaseState | None = None
|
|
3061
3149
|
) -> BaseState | None:
|
|
@@ -3199,29 +3287,6 @@ class StateManagerRedis(StateManager):
|
|
|
3199
3287
|
return state._get_root_state()
|
|
3200
3288
|
return state
|
|
3201
3289
|
|
|
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
3290
|
@override
|
|
3226
3291
|
async def set_state(
|
|
3227
3292
|
self,
|
|
@@ -3248,7 +3313,7 @@ class StateManagerRedis(StateManager):
|
|
|
3248
3313
|
raise LockExpiredError(
|
|
3249
3314
|
f"Lock expired for token {token} while processing. Consider increasing "
|
|
3250
3315
|
f"`app.state_manager.lock_expiration` (currently {self.lock_expiration}) "
|
|
3251
|
-
"or use `@rx.background` decorator for long-running tasks."
|
|
3316
|
+
"or use `@rx.event(background=True)` decorator for long-running tasks."
|
|
3252
3317
|
)
|
|
3253
3318
|
client_token, substate_name = _split_substate_key(token)
|
|
3254
3319
|
# If the substate name on the token doesn't match the instance name, it cannot have a parent.
|
|
@@ -3272,7 +3337,6 @@ class StateManagerRedis(StateManager):
|
|
|
3272
3337
|
# Persist only the given state (parents or substates are excluded by BaseState.__getstate__).
|
|
3273
3338
|
if state._get_was_touched():
|
|
3274
3339
|
pickle_state = state._serialize()
|
|
3275
|
-
self._warn_if_too_large(state, len(pickle_state))
|
|
3276
3340
|
if pickle_state:
|
|
3277
3341
|
await self.redis.set(
|
|
3278
3342
|
_substate_key(client_token, state),
|
|
@@ -3353,7 +3417,7 @@ class StateManagerRedis(StateManager):
|
|
|
3353
3417
|
)
|
|
3354
3418
|
except ResponseError:
|
|
3355
3419
|
# Some redis servers only allow out-of-band configuration, so ignore errors here.
|
|
3356
|
-
if not environment.REFLEX_IGNORE_REDIS_CONFIG_ERROR:
|
|
3420
|
+
if not environment.REFLEX_IGNORE_REDIS_CONFIG_ERROR.get():
|
|
3357
3421
|
raise
|
|
3358
3422
|
async with self.redis.pubsub() as pubsub:
|
|
3359
3423
|
await pubsub.psubscribe(lock_key_channel)
|
|
@@ -3693,6 +3757,29 @@ def serialize_mutable_proxy(mp: MutableProxy):
|
|
|
3693
3757
|
return mp.__wrapped__
|
|
3694
3758
|
|
|
3695
3759
|
|
|
3760
|
+
_orig_json_JSONEncoder_default = json.JSONEncoder.default
|
|
3761
|
+
|
|
3762
|
+
|
|
3763
|
+
def _json_JSONEncoder_default_wrapper(self: json.JSONEncoder, o: Any) -> Any:
|
|
3764
|
+
"""Wrap JSONEncoder.default to handle MutableProxy objects.
|
|
3765
|
+
|
|
3766
|
+
Args:
|
|
3767
|
+
self: the JSONEncoder instance.
|
|
3768
|
+
o: the object to serialize.
|
|
3769
|
+
|
|
3770
|
+
Returns:
|
|
3771
|
+
A JSON-able object.
|
|
3772
|
+
"""
|
|
3773
|
+
try:
|
|
3774
|
+
return o.__wrapped__
|
|
3775
|
+
except AttributeError:
|
|
3776
|
+
pass
|
|
3777
|
+
return _orig_json_JSONEncoder_default(self, o)
|
|
3778
|
+
|
|
3779
|
+
|
|
3780
|
+
json.JSONEncoder.default = _json_JSONEncoder_default_wrapper
|
|
3781
|
+
|
|
3782
|
+
|
|
3696
3783
|
class ImmutableMutableProxy(MutableProxy):
|
|
3697
3784
|
"""A proxy for a mutable object that tracks changes.
|
|
3698
3785
|
|
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."""
|