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.
Files changed (228) hide show
  1. reflex/.templates/jinja/web/pages/custom_component.js.jinja2 +0 -14
  2. reflex/.templates/jinja/web/pages/utils.js.jinja2 +4 -8
  3. reflex/.templates/web/components/shiki/code.js +16 -11
  4. reflex/.templates/web/utils/state.js +29 -21
  5. reflex/__init__.py +4 -0
  6. reflex/__init__.pyi +4 -0
  7. reflex/app.py +148 -154
  8. reflex/app_mixins/lifespan.py +5 -1
  9. reflex/app_mixins/middleware.py +3 -1
  10. reflex/app_mixins/mixin.py +3 -2
  11. reflex/base.py +2 -4
  12. reflex/compiler/compiler.py +111 -37
  13. reflex/components/base/app_wrap.pyi +17 -17
  14. reflex/components/base/bare.py +72 -3
  15. reflex/components/base/body.pyi +17 -17
  16. reflex/components/base/document.pyi +81 -81
  17. reflex/components/base/error_boundary.pyi +25 -18
  18. reflex/components/base/fragment.pyi +17 -17
  19. reflex/components/base/head.pyi +33 -33
  20. reflex/components/base/link.pyi +33 -33
  21. reflex/components/base/meta.pyi +65 -65
  22. reflex/components/base/script.py +4 -4
  23. reflex/components/base/script.pyi +23 -20
  24. reflex/components/component.py +250 -31
  25. reflex/components/core/banner.py +1 -1
  26. reflex/components/core/banner.pyi +81 -81
  27. reflex/components/core/client_side_routing.pyi +33 -33
  28. reflex/components/core/clipboard.py +2 -2
  29. reflex/components/core/clipboard.pyi +24 -18
  30. reflex/components/core/debounce.py +2 -2
  31. reflex/components/core/debounce.pyi +18 -18
  32. reflex/components/core/html.pyi +17 -17
  33. reflex/components/core/upload.py +82 -28
  34. reflex/components/core/upload.pyi +77 -72
  35. reflex/components/datadisplay/code.py +55 -40
  36. reflex/components/datadisplay/code.pyi +46 -44
  37. reflex/components/datadisplay/dataeditor.py +21 -20
  38. reflex/components/datadisplay/dataeditor.pyi +103 -35
  39. reflex/components/datadisplay/shiki_code_block.py +60 -27
  40. reflex/components/datadisplay/shiki_code_block.pyi +86 -65
  41. reflex/components/dynamic.py +9 -5
  42. reflex/components/el/element.pyi +17 -17
  43. reflex/components/el/elements/base.pyi +17 -17
  44. reflex/components/el/elements/forms.py +12 -3
  45. reflex/components/el/elements/forms.pyi +293 -233
  46. reflex/components/el/elements/inline.pyi +449 -449
  47. reflex/components/el/elements/media.pyi +401 -401
  48. reflex/components/el/elements/metadata.pyi +97 -97
  49. reflex/components/el/elements/other.pyi +113 -113
  50. reflex/components/el/elements/scripts.pyi +49 -49
  51. reflex/components/el/elements/sectioning.pyi +241 -241
  52. reflex/components/el/elements/tables.pyi +161 -161
  53. reflex/components/el/elements/typography.pyi +241 -241
  54. reflex/components/gridjs/datatable.pyi +33 -33
  55. reflex/components/lucide/icon.py +1 -1
  56. reflex/components/lucide/icon.pyi +33 -33
  57. reflex/components/markdown/markdown.py +180 -49
  58. reflex/components/markdown/markdown.pyi +36 -19
  59. reflex/components/moment/moment.py +17 -21
  60. reflex/components/moment/moment.pyi +26 -21
  61. reflex/components/next/base.pyi +17 -17
  62. reflex/components/next/image.py +3 -3
  63. reflex/components/next/image.pyi +21 -19
  64. reflex/components/next/link.pyi +17 -17
  65. reflex/components/next/video.pyi +17 -17
  66. reflex/components/plotly/plotly.py +79 -78
  67. reflex/components/plotly/plotly.pyi +91 -41
  68. reflex/components/props.py +34 -0
  69. reflex/components/radix/primitives/accordion.py +15 -8
  70. reflex/components/radix/primitives/accordion.pyi +121 -118
  71. reflex/components/radix/primitives/base.pyi +33 -33
  72. reflex/components/radix/primitives/drawer.py +41 -20
  73. reflex/components/radix/primitives/drawer.pyi +279 -190
  74. reflex/components/radix/primitives/form.py +2 -2
  75. reflex/components/radix/primitives/form.pyi +200 -167
  76. reflex/components/radix/primitives/progress.pyi +81 -81
  77. reflex/components/radix/primitives/slider.pyi +89 -83
  78. reflex/components/radix/themes/base.py +27 -1
  79. reflex/components/radix/themes/base.pyi +286 -113
  80. reflex/components/radix/themes/color_mode.py +17 -9
  81. reflex/components/radix/themes/color_mode.pyi +68 -56
  82. reflex/components/radix/themes/components/alert_dialog.py +8 -5
  83. reflex/components/radix/themes/components/alert_dialog.pyi +125 -117
  84. reflex/components/radix/themes/components/aspect_ratio.pyi +17 -17
  85. reflex/components/radix/themes/components/avatar.py +1 -5
  86. reflex/components/radix/themes/components/avatar.pyi +17 -17
  87. reflex/components/radix/themes/components/badge.py +1 -5
  88. reflex/components/radix/themes/components/badge.pyi +17 -17
  89. reflex/components/radix/themes/components/button.pyi +18 -21
  90. reflex/components/radix/themes/components/callout.py +1 -4
  91. reflex/components/radix/themes/components/callout.pyi +81 -81
  92. reflex/components/radix/themes/components/card.py +1 -3
  93. reflex/components/radix/themes/components/card.pyi +17 -17
  94. reflex/components/radix/themes/components/checkbox.py +4 -8
  95. reflex/components/radix/themes/components/checkbox.pyi +61 -52
  96. reflex/components/radix/themes/components/checkbox_cards.pyi +33 -33
  97. reflex/components/radix/themes/components/checkbox_group.pyi +33 -33
  98. reflex/components/radix/themes/components/context_menu.py +121 -28
  99. reflex/components/radix/themes/components/context_menu.pyi +250 -147
  100. reflex/components/radix/themes/components/data_list.pyi +65 -65
  101. reflex/components/radix/themes/components/dialog.py +11 -11
  102. reflex/components/radix/themes/components/dialog.pyi +135 -120
  103. reflex/components/radix/themes/components/dropdown_menu.py +14 -25
  104. reflex/components/radix/themes/components/dropdown_menu.pyi +157 -145
  105. reflex/components/radix/themes/components/hover_card.py +19 -7
  106. reflex/components/radix/themes/components/hover_card.pyi +102 -67
  107. reflex/components/radix/themes/components/icon_button.pyi +18 -21
  108. reflex/components/radix/themes/components/inset.py +1 -3
  109. reflex/components/radix/themes/components/inset.pyi +17 -17
  110. reflex/components/radix/themes/components/popover.py +22 -13
  111. reflex/components/radix/themes/components/popover.pyi +98 -72
  112. reflex/components/radix/themes/components/progress.pyi +17 -17
  113. reflex/components/radix/themes/components/radio.pyi +17 -17
  114. reflex/components/radix/themes/components/radio_cards.py +2 -2
  115. reflex/components/radix/themes/components/radio_cards.pyi +37 -34
  116. reflex/components/radix/themes/components/radio_group.py +3 -7
  117. reflex/components/radix/themes/components/radio_group.pyi +69 -66
  118. reflex/components/radix/themes/components/scroll_area.py +1 -3
  119. reflex/components/radix/themes/components/scroll_area.pyi +17 -17
  120. reflex/components/radix/themes/components/segmented_control.pyi +37 -34
  121. reflex/components/radix/themes/components/select.py +7 -11
  122. reflex/components/radix/themes/components/select.pyi +175 -154
  123. reflex/components/radix/themes/components/separator.py +1 -4
  124. reflex/components/radix/themes/components/separator.pyi +17 -17
  125. reflex/components/radix/themes/components/skeleton.pyi +17 -17
  126. reflex/components/radix/themes/components/slider.py +12 -21
  127. reflex/components/radix/themes/components/slider.pyi +47 -25
  128. reflex/components/radix/themes/components/spinner.py +1 -4
  129. reflex/components/radix/themes/components/spinner.pyi +17 -17
  130. reflex/components/radix/themes/components/switch.py +3 -6
  131. reflex/components/radix/themes/components/switch.pyi +21 -18
  132. reflex/components/radix/themes/components/table.py +21 -5
  133. reflex/components/radix/themes/components/table.pyi +392 -116
  134. reflex/components/radix/themes/components/tabs.py +3 -6
  135. reflex/components/radix/themes/components/tabs.pyi +89 -83
  136. reflex/components/radix/themes/components/text_area.py +1 -5
  137. reflex/components/radix/themes/components/text_area.pyi +43 -20
  138. reflex/components/radix/themes/components/text_field.py +1 -5
  139. reflex/components/radix/themes/components/text_field.pyi +101 -55
  140. reflex/components/radix/themes/components/tooltip.py +5 -7
  141. reflex/components/radix/themes/components/tooltip.pyi +25 -22
  142. reflex/components/radix/themes/layout/base.py +2 -27
  143. reflex/components/radix/themes/layout/base.pyi +82 -82
  144. reflex/components/radix/themes/layout/box.pyi +17 -17
  145. reflex/components/radix/themes/layout/center.pyi +17 -17
  146. reflex/components/radix/themes/layout/container.pyi +17 -17
  147. reflex/components/radix/themes/layout/flex.py +1 -6
  148. reflex/components/radix/themes/layout/flex.pyi +17 -17
  149. reflex/components/radix/themes/layout/grid.py +1 -6
  150. reflex/components/radix/themes/layout/grid.pyi +17 -17
  151. reflex/components/radix/themes/layout/list.py +20 -15
  152. reflex/components/radix/themes/layout/list.pyi +175 -92
  153. reflex/components/radix/themes/layout/section.pyi +17 -17
  154. reflex/components/radix/themes/layout/spacer.pyi +17 -17
  155. reflex/components/radix/themes/layout/stack.py +6 -6
  156. reflex/components/radix/themes/layout/stack.pyi +91 -62
  157. reflex/components/radix/themes/typography/blockquote.py +2 -8
  158. reflex/components/radix/themes/typography/blockquote.pyi +17 -17
  159. reflex/components/radix/themes/typography/code.py +4 -10
  160. reflex/components/radix/themes/typography/code.pyi +19 -18
  161. reflex/components/radix/themes/typography/heading.py +4 -11
  162. reflex/components/radix/themes/typography/heading.pyi +19 -18
  163. reflex/components/radix/themes/typography/link.py +4 -10
  164. reflex/components/radix/themes/typography/link.pyi +19 -18
  165. reflex/components/radix/themes/typography/text.py +4 -11
  166. reflex/components/radix/themes/typography/text.pyi +115 -114
  167. reflex/components/react_player/audio.pyi +58 -33
  168. reflex/components/react_player/react_player.py +17 -17
  169. reflex/components/react_player/react_player.pyi +55 -33
  170. reflex/components/react_player/video.pyi +58 -33
  171. reflex/components/recharts/cartesian.py +45 -45
  172. reflex/components/recharts/cartesian.pyi +389 -304
  173. reflex/components/recharts/charts.py +22 -22
  174. reflex/components/recharts/charts.pyi +226 -179
  175. reflex/components/recharts/general.py +26 -27
  176. reflex/components/recharts/general.pyi +106 -99
  177. reflex/components/recharts/polar.py +33 -33
  178. reflex/components/recharts/polar.pyi +70 -64
  179. reflex/components/recharts/recharts.pyi +33 -33
  180. reflex/components/sonner/toast.py +9 -36
  181. reflex/components/sonner/toast.pyi +20 -24
  182. reflex/components/suneditor/editor.py +8 -8
  183. reflex/components/suneditor/editor.pyi +50 -25
  184. reflex/components/tags/iter_tag.py +1 -10
  185. reflex/components/tags/tag.py +1 -4
  186. reflex/config.py +198 -35
  187. reflex/constants/__init__.py +4 -16
  188. reflex/constants/base.py +7 -14
  189. reflex/constants/colors.py +0 -1
  190. reflex/constants/installer.py +12 -7
  191. reflex/constants/state.py +4 -0
  192. reflex/custom_components/custom_components.py +6 -6
  193. reflex/event.py +486 -241
  194. reflex/experimental/client_state.py +9 -9
  195. reflex/experimental/layout.py +2 -2
  196. reflex/experimental/layout.pyi +95 -87
  197. reflex/experimental/misc.py +1 -1
  198. reflex/istate/__init__.py +1 -0
  199. reflex/istate/proxy.py +33 -0
  200. reflex/istate/wrappers.py +27 -0
  201. reflex/model.py +7 -7
  202. reflex/page.py +2 -1
  203. reflex/reflex.py +142 -8
  204. reflex/state.py +127 -46
  205. reflex/testing.py +9 -7
  206. reflex/utils/console.py +0 -1
  207. reflex/utils/exceptions.py +31 -3
  208. reflex/utils/exec.py +33 -14
  209. reflex/utils/format.py +15 -12
  210. reflex/utils/net.py +1 -1
  211. reflex/utils/path_ops.py +2 -2
  212. reflex/utils/prerequisites.py +82 -46
  213. reflex/utils/pyi_generator.py +63 -20
  214. reflex/utils/registry.py +1 -1
  215. reflex/utils/serializers.py +75 -36
  216. reflex/utils/telemetry.py +3 -2
  217. reflex/utils/types.py +125 -10
  218. reflex/vars/base.py +131 -119
  219. reflex/vars/function.py +59 -12
  220. reflex/vars/number.py +3 -1
  221. reflex/vars/object.py +30 -24
  222. reflex/vars/sequence.py +7 -7
  223. {reflex-0.6.4a3.dist-info → reflex-0.6.5a1.dist-info}/METADATA +3 -3
  224. reflex-0.6.5a1.dist-info/RECORD +394 -0
  225. reflex-0.6.4a3.dist-info/RECORD +0 -391
  226. {reflex-0.6.4a3.dist-info → reflex-0.6.5a1.dist-info}/LICENSE +0 -0
  227. {reflex-0.6.4a3.dist-info → reflex-0.6.5a1.dist-info}/WHEEL +0 -0
  228. {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
- os.environ[constants.ENV_MODE_ENV_VAR] = env.value
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=constants.ENV_FRONTEND_ONLY_ENV_VAR,
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=constants.ENV_BACKEND_ONLY_ENV_VAR,
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
- os.environ[constants.ENV_BACKEND_ONLY_ENV_VAR] = str(backend).lower()
306
- os.environ[constants.ENV_FRONTEND_ONLY_ENV_VAR] = str(frontend).lower()
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(f"Unable to authenticate. Please try again or contact support.")
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
- os.environ[constants.SKIP_COMPILE_ENV_VAR] = "yes"
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 get_origin, override
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 pass to the Pydantic init method.
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__(*args, **kwargs)
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 isinstance(result, of_type):
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.get_setter_name(include_state=False)
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.get_setter())
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.get_default_value()
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).get_default_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
- return pickle.dumps((self._to_schema(), self))
2066
- except (pickle.PicklingError, AttributeError) as og_pickle_error:
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 (pickle.PicklingError, TypeError, ValueError) as ex:
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
- types.FunctionType | types.ModuleType | str | functools.partial[Any]
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
- types.FunctionType | types.ModuleType | str | functools.partial[Any]
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 os.environ.get("APP_HARNESS_HEADLESS"):
620
+ if environment.APP_HARNESS_HEADLESS.get():
619
621
  want_headless = True
620
622
  if driver_clz is None:
621
- requested_driver = os.environ.get("APP_HARNESS_DRIVER", "Chrome")
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 := os.environ.get("APP_HARNESS_DRIVER_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
- os.environ[reflex.constants.SKIP_COMPILE_ENV_VAR] = "yes"
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
- os.environ.pop(reflex.constants.SKIP_COMPILE_ENV_VAR, None)
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
@@ -191,7 +191,6 @@ def ask(
191
191
  def progress():
192
192
  """Create a new progress bar.
193
193
 
194
-
195
194
  Returns:
196
195
  A new progress bar.
197
196
  """
@@ -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 EventHandlerArgMismatch(ReflexError, TypeError):
93
- """Raised when the number of args accepted by an EventHandler is differs from that provided by the event trigger."""
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 accepted by a lambda differs from that provided by the event trigger."""
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
+ )