reflex 0.6.4a3__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.

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 +90 -28
  34. reflex/components/core/upload.pyi +128 -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 +30 -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 +133 -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.5.dist-info}/METADATA +3 -3
  224. reflex-0.6.5.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.5.dist-info}/LICENSE +0 -0
  227. {reflex-0.6.4a3.dist-info → reflex-0.6.5.dist-info}/WHEEL +0 -0
  228. {reflex-0.6.4a3.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
- 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,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
- ClientStorageBase,
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 get_origin, override
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 pass to the Pydantic init method.
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__(*args, **kwargs)
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 isinstance(result, of_type):
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.get_setter_name(include_state=False)
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.get_setter())
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.get_default_value()
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).get_default_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
- return pickle.dumps((self._to_schema(), self))
2066
- except (pickle.PicklingError, AttributeError) as og_pickle_error:
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 (pickle.PicklingError, TypeError, ValueError) as ex:
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
- 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
  """