reflex 0.7.14a6__py3-none-any.whl → 0.8.0a1__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 (206) hide show
  1. reflex/.templates/jinja/app/rxconfig.py.jinja2 +4 -1
  2. reflex/.templates/jinja/web/package.json.jinja2 +1 -1
  3. reflex/.templates/jinja/web/pages/_app.js.jinja2 +16 -10
  4. reflex/.templates/jinja/web/pages/_document.js.jinja2 +1 -1
  5. reflex/.templates/jinja/web/pages/base_page.js.jinja2 +0 -1
  6. reflex/.templates/jinja/web/utils/context.js.jinja2 +25 -6
  7. reflex/.templates/web/app/entry.client.js +8 -0
  8. reflex/.templates/web/app/routes.js +10 -0
  9. reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js +12 -32
  10. reflex/.templates/web/postcss.config.js +1 -1
  11. reflex/.templates/web/react-router.config.js +6 -0
  12. reflex/.templates/web/utils/client_side_routing.js +21 -19
  13. reflex/.templates/web/utils/react-theme.js +89 -0
  14. reflex/.templates/web/utils/state.js +155 -67
  15. reflex/.templates/web/vite.config.js +32 -0
  16. reflex/__init__.py +1 -6
  17. reflex/__init__.pyi +0 -4
  18. reflex/app.py +52 -115
  19. reflex/base.py +1 -87
  20. reflex/compiler/compiler.py +40 -3
  21. reflex/compiler/utils.py +54 -28
  22. reflex/components/__init__.py +0 -2
  23. reflex/components/__init__.pyi +0 -3
  24. reflex/components/base/__init__.py +1 -5
  25. reflex/components/base/__init__.pyi +4 -6
  26. reflex/components/base/app_wrap.pyi +5 -4
  27. reflex/components/base/body.pyi +5 -4
  28. reflex/components/base/document.py +18 -14
  29. reflex/components/base/document.pyi +83 -27
  30. reflex/components/base/error_boundary.pyi +5 -4
  31. reflex/components/base/fragment.pyi +5 -4
  32. reflex/components/base/link.pyi +9 -7
  33. reflex/components/base/meta.pyi +17 -13
  34. reflex/components/base/script.py +60 -58
  35. reflex/components/base/script.pyi +246 -31
  36. reflex/components/base/strict_mode.pyi +5 -4
  37. reflex/components/component.py +109 -194
  38. reflex/components/core/__init__.py +1 -0
  39. reflex/components/core/__init__.pyi +1 -0
  40. reflex/components/core/auto_scroll.pyi +5 -4
  41. reflex/components/core/banner.pyi +25 -19
  42. reflex/components/core/client_side_routing.py +7 -6
  43. reflex/components/core/client_side_routing.pyi +6 -56
  44. reflex/components/core/clipboard.pyi +5 -4
  45. reflex/components/core/debounce.py +1 -0
  46. reflex/components/core/debounce.pyi +5 -4
  47. reflex/components/core/foreach.py +3 -2
  48. reflex/components/core/helmet.py +14 -0
  49. reflex/components/{next/base.pyi → core/helmet.pyi} +10 -7
  50. reflex/components/core/html.pyi +5 -4
  51. reflex/components/core/sticky.pyi +17 -13
  52. reflex/components/core/upload.py +2 -1
  53. reflex/components/core/upload.pyi +21 -16
  54. reflex/components/datadisplay/code.pyi +9 -7
  55. reflex/components/datadisplay/dataeditor.pyi +5 -4
  56. reflex/components/datadisplay/shiki_code_block.pyi +13 -10
  57. reflex/components/dynamic.py +4 -5
  58. reflex/components/el/element.pyi +5 -4
  59. reflex/components/el/elements/base.pyi +5 -4
  60. reflex/components/el/elements/forms.pyi +69 -52
  61. reflex/components/el/elements/inline.pyi +113 -85
  62. reflex/components/el/elements/media.pyi +105 -79
  63. reflex/components/el/elements/metadata.pyi +25 -19
  64. reflex/components/el/elements/other.pyi +29 -22
  65. reflex/components/el/elements/scripts.pyi +13 -10
  66. reflex/components/el/elements/sectioning.pyi +61 -46
  67. reflex/components/el/elements/tables.pyi +41 -31
  68. reflex/components/el/elements/typography.pyi +61 -46
  69. reflex/components/field.py +175 -0
  70. reflex/components/gridjs/datatable.py +2 -2
  71. reflex/components/gridjs/datatable.pyi +11 -9
  72. reflex/components/lucide/icon.py +6 -2
  73. reflex/components/lucide/icon.pyi +15 -10
  74. reflex/components/markdown/markdown.pyi +5 -4
  75. reflex/components/moment/moment.pyi +5 -4
  76. reflex/components/plotly/plotly.pyi +19 -10
  77. reflex/components/props.py +376 -27
  78. reflex/components/radix/primitives/accordion.py +8 -1
  79. reflex/components/radix/primitives/accordion.pyi +29 -22
  80. reflex/components/radix/primitives/base.pyi +9 -7
  81. reflex/components/radix/primitives/drawer.pyi +45 -34
  82. reflex/components/radix/primitives/form.pyi +41 -31
  83. reflex/components/radix/primitives/progress.pyi +21 -16
  84. reflex/components/radix/primitives/slider.pyi +21 -16
  85. reflex/components/radix/themes/base.py +3 -3
  86. reflex/components/radix/themes/base.pyi +33 -25
  87. reflex/components/radix/themes/color_mode.pyi +13 -10
  88. reflex/components/radix/themes/components/alert_dialog.pyi +29 -22
  89. reflex/components/radix/themes/components/aspect_ratio.pyi +5 -4
  90. reflex/components/radix/themes/components/avatar.pyi +5 -4
  91. reflex/components/radix/themes/components/badge.pyi +5 -4
  92. reflex/components/radix/themes/components/button.pyi +5 -4
  93. reflex/components/radix/themes/components/callout.pyi +21 -16
  94. reflex/components/radix/themes/components/card.pyi +5 -4
  95. reflex/components/radix/themes/components/checkbox.pyi +13 -10
  96. reflex/components/radix/themes/components/checkbox_cards.pyi +9 -7
  97. reflex/components/radix/themes/components/checkbox_group.pyi +9 -7
  98. reflex/components/radix/themes/components/context_menu.pyi +53 -40
  99. reflex/components/radix/themes/components/data_list.pyi +17 -13
  100. reflex/components/radix/themes/components/dialog.pyi +29 -22
  101. reflex/components/radix/themes/components/dropdown_menu.pyi +33 -25
  102. reflex/components/radix/themes/components/hover_card.pyi +17 -13
  103. reflex/components/radix/themes/components/icon_button.pyi +5 -4
  104. reflex/components/radix/themes/components/inset.pyi +5 -4
  105. reflex/components/radix/themes/components/popover.pyi +17 -13
  106. reflex/components/radix/themes/components/progress.pyi +5 -4
  107. reflex/components/radix/themes/components/radio.pyi +5 -4
  108. reflex/components/radix/themes/components/radio_cards.pyi +9 -7
  109. reflex/components/radix/themes/components/radio_group.pyi +17 -13
  110. reflex/components/radix/themes/components/scroll_area.pyi +5 -4
  111. reflex/components/radix/themes/components/segmented_control.pyi +9 -7
  112. reflex/components/radix/themes/components/select.pyi +37 -28
  113. reflex/components/radix/themes/components/separator.pyi +5 -4
  114. reflex/components/radix/themes/components/skeleton.pyi +5 -4
  115. reflex/components/radix/themes/components/slider.pyi +5 -4
  116. reflex/components/radix/themes/components/spinner.pyi +5 -4
  117. reflex/components/radix/themes/components/switch.pyi +5 -4
  118. reflex/components/radix/themes/components/table.pyi +29 -22
  119. reflex/components/radix/themes/components/tabs.pyi +21 -16
  120. reflex/components/radix/themes/components/text_area.pyi +5 -4
  121. reflex/components/radix/themes/components/text_field.pyi +13 -10
  122. reflex/components/radix/themes/components/tooltip.pyi +5 -4
  123. reflex/components/radix/themes/layout/base.pyi +5 -4
  124. reflex/components/radix/themes/layout/box.pyi +5 -4
  125. reflex/components/radix/themes/layout/center.pyi +5 -4
  126. reflex/components/radix/themes/layout/container.pyi +5 -4
  127. reflex/components/radix/themes/layout/flex.pyi +5 -4
  128. reflex/components/radix/themes/layout/grid.pyi +5 -4
  129. reflex/components/radix/themes/layout/list.pyi +21 -16
  130. reflex/components/radix/themes/layout/section.pyi +5 -4
  131. reflex/components/radix/themes/layout/spacer.pyi +5 -4
  132. reflex/components/radix/themes/layout/stack.pyi +13 -10
  133. reflex/components/radix/themes/typography/blockquote.pyi +5 -4
  134. reflex/components/radix/themes/typography/code.pyi +5 -4
  135. reflex/components/radix/themes/typography/heading.pyi +5 -4
  136. reflex/components/radix/themes/typography/link.py +42 -9
  137. reflex/components/radix/themes/typography/link.pyi +311 -6
  138. reflex/components/radix/themes/typography/text.pyi +29 -22
  139. reflex/components/react_player/audio.pyi +5 -4
  140. reflex/components/react_player/react_player.pyi +5 -4
  141. reflex/components/react_player/video.pyi +5 -4
  142. reflex/components/recharts/cartesian.py +2 -1
  143. reflex/components/recharts/cartesian.pyi +65 -46
  144. reflex/components/recharts/charts.py +4 -2
  145. reflex/components/recharts/charts.pyi +36 -24
  146. reflex/components/recharts/general.pyi +24 -18
  147. reflex/components/recharts/polar.py +8 -4
  148. reflex/components/recharts/polar.pyi +16 -10
  149. reflex/components/recharts/recharts.pyi +9 -7
  150. reflex/components/sonner/toast.py +2 -2
  151. reflex/components/sonner/toast.pyi +7 -6
  152. reflex/config.py +3 -77
  153. reflex/constants/__init__.py +1 -0
  154. reflex/constants/base.py +38 -8
  155. reflex/constants/compiler.py +4 -2
  156. reflex/constants/event.py +1 -0
  157. reflex/constants/installer.py +23 -16
  158. reflex/constants/state.py +2 -0
  159. reflex/custom_components/custom_components.py +0 -14
  160. reflex/environment.py +1 -1
  161. reflex/event.py +178 -81
  162. reflex/experimental/__init__.py +0 -30
  163. reflex/istate/proxy.py +5 -3
  164. reflex/page.py +0 -27
  165. reflex/plugins/__init__.py +3 -2
  166. reflex/plugins/base.py +5 -1
  167. reflex/plugins/shared_tailwind.py +158 -0
  168. reflex/plugins/sitemap.py +206 -0
  169. reflex/plugins/tailwind_v3.py +13 -106
  170. reflex/plugins/tailwind_v4.py +15 -108
  171. reflex/reflex.py +1 -0
  172. reflex/state.py +134 -140
  173. reflex/testing.py +57 -9
  174. reflex/utils/build.py +9 -69
  175. reflex/utils/exec.py +59 -161
  176. reflex/utils/export.py +1 -1
  177. reflex/utils/imports.py +0 -4
  178. reflex/utils/misc.py +28 -0
  179. reflex/utils/prerequisites.py +62 -59
  180. reflex/utils/processes.py +6 -6
  181. reflex/utils/pyi_generator.py +21 -9
  182. reflex/utils/serializers.py +14 -1
  183. reflex/utils/types.py +196 -61
  184. reflex/vars/__init__.py +2 -0
  185. reflex/vars/base.py +367 -134
  186. {reflex-0.7.14a6.dist-info → reflex-0.8.0a1.dist-info}/METADATA +12 -5
  187. {reflex-0.7.14a6.dist-info → reflex-0.8.0a1.dist-info}/RECORD +190 -197
  188. reflex/.templates/web/next.config.js +0 -7
  189. reflex/components/base/head.py +0 -20
  190. reflex/components/base/head.pyi +0 -116
  191. reflex/components/next/__init__.py +0 -10
  192. reflex/components/next/base.py +0 -7
  193. reflex/components/next/image.py +0 -117
  194. reflex/components/next/image.pyi +0 -94
  195. reflex/components/next/link.py +0 -20
  196. reflex/components/next/link.pyi +0 -67
  197. reflex/components/next/video.py +0 -38
  198. reflex/components/next/video.pyi +0 -68
  199. reflex/components/suneditor/__init__.py +0 -5
  200. reflex/components/suneditor/editor.py +0 -269
  201. reflex/components/suneditor/editor.pyi +0 -199
  202. reflex/experimental/layout.py +0 -254
  203. reflex/experimental/layout.pyi +0 -814
  204. {reflex-0.7.14a6.dist-info → reflex-0.8.0a1.dist-info}/WHEEL +0 -0
  205. {reflex-0.7.14a6.dist-info → reflex-0.8.0a1.dist-info}/entry_points.txt +0 -0
  206. {reflex-0.7.14a6.dist-info → reflex-0.8.0a1.dist-info}/licenses/LICENSE +0 -0
@@ -39,9 +39,7 @@ from reflex.compiler import templates
39
39
  from reflex.config import Config, get_config
40
40
  from reflex.environment import environment
41
41
  from reflex.utils import console, net, path_ops, processes, redir
42
- from reflex.utils.decorator import once
43
42
  from reflex.utils.exceptions import SystemPackageMissingError
44
- from reflex.utils.format import format_library_name
45
43
  from reflex.utils.registry import get_npm_registry
46
44
 
47
45
  if typing.TYPE_CHECKING:
@@ -74,7 +72,7 @@ class CpuInfo:
74
72
 
75
73
 
76
74
  def get_web_dir() -> Path:
77
- """Get the working directory for the next.js commands.
75
+ """Get the working directory for the frontend.
78
76
 
79
77
  Can be overridden with REFLEX_WEB_WORKDIR.
80
78
 
@@ -334,13 +332,14 @@ def npm_escape_hatch() -> bool:
334
332
 
335
333
 
336
334
  def _check_app_name(config: Config):
337
- """Check if the app name is set in the config.
335
+ """Check if the app name is valid and matches the folder structure.
338
336
 
339
337
  Args:
340
338
  config: The config object.
341
339
 
342
340
  Raises:
343
- RuntimeError: If the app name is not set in the config.
341
+ RuntimeError: If the app name is not set, folder doesn't exist, or doesn't match config.
342
+ ModuleNotFoundError: If the app_name is not importable (i.e., not a valid Python package, folder structure being wrong).
344
343
  """
345
344
  if not config.app_name:
346
345
  msg = (
@@ -349,6 +348,21 @@ def _check_app_name(config: Config):
349
348
  )
350
349
  raise RuntimeError(msg)
351
350
 
351
+ from reflex.utils.misc import with_cwd_in_syspath
352
+
353
+ with with_cwd_in_syspath():
354
+ try:
355
+ mod_spec = importlib.util.find_spec(config.module)
356
+ except ModuleNotFoundError:
357
+ mod_spec = None
358
+ if mod_spec is None:
359
+ msg = f"Module {config.module} not found. "
360
+ if config.app_module_import is not None:
361
+ msg += f"Ensure app_module_import='{config.app_module_import}' in rxconfig.py matches your folder structure."
362
+ else:
363
+ msg += f"Ensure app_name='{config.app_name}' in rxconfig.py matches your folder structure."
364
+ raise ModuleNotFoundError(msg)
365
+
352
366
 
353
367
  def get_app(reload: bool = False) -> ModuleType:
354
368
  """Get the app module based on the default config.
@@ -439,7 +453,7 @@ def validate_app(
439
453
 
440
454
  def get_compiled_app(
441
455
  reload: bool = False,
442
- export: bool = False,
456
+ prerender_routes: bool = False,
443
457
  dry_run: bool = False,
444
458
  check_if_schema_up_to_date: bool = False,
445
459
  ) -> ModuleType:
@@ -447,7 +461,7 @@ def get_compiled_app(
447
461
 
448
462
  Args:
449
463
  reload: Re-import the app module from disk
450
- export: Compile the app for export
464
+ prerender_routes: Whether to prerender routes.
451
465
  dry_run: If True, do not write the compiled app to disk.
452
466
  check_if_schema_up_to_date: If True, check if the schema is up to date.
453
467
 
@@ -460,13 +474,13 @@ def get_compiled_app(
460
474
  # For py3.9 compatibility when redis is used, we MUST add any decorator pages
461
475
  # before compiling the app in a thread to avoid event loop error (REF-2172).
462
476
  app._apply_decorated_pages()
463
- app._compile(export=export, dry_run=dry_run)
477
+ app._compile(prerender_routes=prerender_routes, dry_run=dry_run)
464
478
  return app_module
465
479
 
466
480
 
467
481
  def compile_app(
468
482
  reload: bool = False,
469
- export: bool = False,
483
+ prerender_routes: bool = False,
470
484
  dry_run: bool = False,
471
485
  check_if_schema_up_to_date: bool = False,
472
486
  ) -> None:
@@ -474,13 +488,13 @@ def compile_app(
474
488
 
475
489
  Args:
476
490
  reload: Re-import the app module from disk
477
- export: Compile the app for export
491
+ prerender_routes: Whether to prerender routes.
478
492
  dry_run: If True, do not write the compiled app to disk.
479
493
  check_if_schema_up_to_date: If True, check if the schema is up to date.
480
494
  """
481
495
  get_compiled_app(
482
496
  reload=reload,
483
- export=export,
497
+ prerender_routes=prerender_routes,
484
498
  dry_run=dry_run,
485
499
  check_if_schema_up_to_date=check_if_schema_up_to_date,
486
500
  )
@@ -529,20 +543,26 @@ def _can_colorize() -> bool:
529
543
 
530
544
 
531
545
  def compile_or_validate_app(
532
- compile: bool = False, check_if_schema_up_to_date: bool = False
546
+ compile: bool = False,
547
+ check_if_schema_up_to_date: bool = False,
548
+ prerender_routes: bool = False,
533
549
  ) -> bool:
534
550
  """Compile or validate the app module based on the default config.
535
551
 
536
552
  Args:
537
553
  compile: Whether to compile the app.
538
554
  check_if_schema_up_to_date: If True, check if the schema is up to date.
555
+ prerender_routes: Whether to prerender routes.
539
556
 
540
557
  Returns:
541
558
  If the app is compiled successfully.
542
559
  """
543
560
  try:
544
561
  if compile:
545
- compile_app(check_if_schema_up_to_date=check_if_schema_up_to_date)
562
+ compile_app(
563
+ check_if_schema_up_to_date=check_if_schema_up_to_date,
564
+ prerender_routes=prerender_routes,
565
+ )
546
566
  else:
547
567
  validate_app(check_if_schema_up_to_date=check_if_schema_up_to_date)
548
568
  except Exception as e:
@@ -1017,29 +1037,19 @@ def initialize_web_directory():
1017
1037
  console.debug("Initializing the public directory.")
1018
1038
  path_ops.mkdir(get_web_dir() / constants.Dirs.PUBLIC)
1019
1039
 
1020
- console.debug("Initializing the next.config.js file.")
1021
- update_next_config()
1040
+ console.debug("Initializing the react-router.config.js file.")
1041
+ update_react_router_config()
1022
1042
 
1023
1043
  console.debug("Initializing the reflex.json file.")
1024
1044
  # Initialize the reflex json file.
1025
1045
  init_reflex_json(project_hash=project_hash)
1026
1046
 
1027
1047
 
1028
- @once
1029
- def _turbopack_flag() -> str:
1030
- return " --turbopack" if environment.REFLEX_USE_TURBOPACK.get() else ""
1031
-
1032
-
1033
1048
  def _compile_package_json():
1034
1049
  return templates.PACKAGE_JSON.render(
1035
1050
  scripts={
1036
- "dev": constants.PackageJson.Commands.DEV.format(flags=_turbopack_flag()),
1037
- "export": constants.PackageJson.Commands.EXPORT.format(
1038
- flags=_turbopack_flag()
1039
- ),
1040
- "export_sitemap": constants.PackageJson.Commands.EXPORT_SITEMAP.format(
1041
- flags=_turbopack_flag()
1042
- ),
1051
+ "dev": constants.PackageJson.Commands.DEV,
1052
+ "export": constants.PackageJson.Commands.EXPORT,
1043
1053
  "prod": constants.PackageJson.Commands.PROD,
1044
1054
  },
1045
1055
  dependencies=constants.PackageJson.DEPENDENCIES,
@@ -1107,50 +1117,43 @@ def init_reflex_json(project_hash: int | None):
1107
1117
  path_ops.update_json_file(get_web_dir() / constants.Reflex.JSON, reflex_json)
1108
1118
 
1109
1119
 
1110
- def update_next_config(
1111
- export: bool = False, transpile_packages: list[str] | None = None
1112
- ):
1113
- """Update Next.js config from Reflex config.
1120
+ def update_react_router_config(prerender_routes: bool = False):
1121
+ """Update react-router.config.js config from Reflex config.
1114
1122
 
1115
1123
  Args:
1116
- export: if the method run during reflex export.
1117
- transpile_packages: list of packages to transpile via next.config.js.
1124
+ prerender_routes: Whether to enable prerendering of routes.
1118
1125
  """
1119
- next_config_file = get_web_dir() / constants.Next.CONFIG_FILE
1126
+ react_router_config_file_path = get_web_dir() / constants.ReactRouter.CONFIG_FILE
1120
1127
 
1121
- next_config = _update_next_config(
1122
- get_config(), export=export, transpile_packages=transpile_packages
1128
+ react_router_config = _update_react_router_config(
1129
+ get_config(), prerender_routes=prerender_routes
1123
1130
  )
1124
1131
 
1125
- # Overwriting the next.config.js triggers a full server reload, so make sure
1132
+ # Overwriting the config file triggers a full server reload, so make sure
1126
1133
  # there is actually a diff.
1127
- orig_next_config = next_config_file.read_text() if next_config_file.exists() else ""
1128
- if orig_next_config != next_config:
1129
- next_config_file.write_text(next_config)
1134
+ orig_next_config = (
1135
+ react_router_config_file_path.read_text()
1136
+ if react_router_config_file_path.exists()
1137
+ else ""
1138
+ )
1139
+ if orig_next_config != react_router_config:
1140
+ react_router_config_file_path.write_text(react_router_config)
1130
1141
 
1131
1142
 
1132
- def _update_next_config(
1133
- config: Config, export: bool = False, transpile_packages: list[str] | None = None
1134
- ):
1135
- next_config = {
1136
- "basePath": config.frontend_path or "",
1137
- "compress": config.next_compression,
1138
- "trailingSlash": True,
1139
- "staticPageGenerationTimeout": config.static_page_generation_timeout,
1143
+ def _update_react_router_config(config: Config, prerender_routes: bool = False):
1144
+ react_router_config = {
1145
+ "basename": "/" + (config.frontend_path or "").removeprefix("/"),
1146
+ "future": {
1147
+ "unstable_optimizeDeps": True,
1148
+ },
1149
+ "ssr": False,
1140
1150
  }
1141
- if not config.next_dev_indicators:
1142
- next_config["devIndicators"] = False
1143
1151
 
1144
- if transpile_packages:
1145
- next_config["transpilePackages"] = list(
1146
- dict.fromkeys([format_library_name(p) for p in transpile_packages])
1147
- )
1148
- if export:
1149
- next_config["output"] = "export"
1150
- next_config["distDir"] = constants.Dirs.STATIC
1152
+ if prerender_routes:
1153
+ react_router_config["prerender"] = True
1154
+ react_router_config["build"] = constants.Dirs.BUILD_DIR
1151
1155
 
1152
- next_config_json = re.sub(r'"([^"]+)"(?=:)', r"\1", json.dumps(next_config))
1153
- return f"module.exports = {next_config_json};"
1156
+ return f"export default {json.dumps(react_router_config)};"
1154
1157
 
1155
1158
 
1156
1159
  def remove_existing_bun_installation():
reflex/utils/processes.py CHANGED
@@ -412,12 +412,12 @@ def show_progress(message: str, process: subprocess.Popen, checkpoints: list[str
412
412
  task = progress.add_task(f"{message}: ", total=len(checkpoints))
413
413
  for line in stream_logs(message, process, progress=progress):
414
414
  # Check for special strings and update the progress bar.
415
- for special_string in checkpoints:
416
- if special_string in line:
417
- progress.update(task, advance=1)
418
- if special_string == checkpoints[-1]:
419
- progress.update(task, completed=len(checkpoints))
420
- break
415
+ special_string = checkpoints[0]
416
+ if special_string in line:
417
+ progress.update(task, advance=1)
418
+ checkpoints.pop(0)
419
+ if not checkpoints:
420
+ break
421
421
 
422
422
 
423
423
  def atexit_handler():
@@ -90,6 +90,7 @@ DEFAULT_IMPORTS = {
90
90
  "EventSpec",
91
91
  "EventType",
92
92
  "KeyInputInfo",
93
+ "PointerEventInfo",
93
94
  ],
94
95
  "reflex.style": ["Style"],
95
96
  "reflex.vars.base": ["Var"],
@@ -366,7 +367,7 @@ def _extract_class_props_as_ast_nodes(
366
367
  all_props = []
367
368
  kwargs = []
368
369
  for target_class in clzs:
369
- event_triggers = target_class._create([]).get_event_triggers()
370
+ event_triggers = target_class.get_event_triggers()
370
371
  # Import from the target class to ensure type hints are resolvable.
371
372
  exec(f"from {target_class.__module__} import *", type_hint_globals)
372
373
  for name, value in target_class.__annotations__.items():
@@ -409,7 +410,7 @@ def _extract_class_props_as_ast_nodes(
409
410
  )
410
411
  ),
411
412
  ),
412
- ast.Constant(value=default),
413
+ ast.Constant(value=default), # pyright: ignore [reportArgumentType]
413
414
  )
414
415
  )
415
416
  return kwargs
@@ -444,7 +445,11 @@ def type_to_ast(typ: Any, cls: type) -> ast.expr:
444
445
 
445
446
  if all(a == b for a, b in zipped) and len(typ_parts) == len(cls_parts):
446
447
  return ast.Name(id=typ.__name__)
447
-
448
+ if (
449
+ typ.__module__ in DEFAULT_IMPORTS
450
+ and typ.__name__ in DEFAULT_IMPORTS[typ.__module__]
451
+ ):
452
+ return ast.Name(id=typ.__name__)
448
453
  return ast.Name(id=typ.__module__ + "." + typ.__name__)
449
454
  return ast.Name(id=typ.__name__)
450
455
  if hasattr(typ, "_name"):
@@ -607,7 +612,7 @@ def _generate_component_create_functiondef(
607
612
  return ast.Name(id=f"{' | '.join(map(ast.unparse, all_count_args_type))}")
608
613
  return ast.Name(id="EventType[Any]")
609
614
 
610
- event_triggers = clz._create([]).get_event_triggers()
615
+ event_triggers = clz.get_event_triggers()
611
616
 
612
617
  # event handler kwargs
613
618
  kwargs.extend(
@@ -1337,12 +1342,19 @@ class PyiGenerator:
1337
1342
 
1338
1343
 
1339
1344
  if __name__ == "__main__":
1345
+ import argparse
1346
+
1347
+ parser = argparse.ArgumentParser(description="Generate .pyi stub files")
1348
+ parser.add_argument(
1349
+ "targets",
1350
+ nargs="*",
1351
+ default=["reflex/components", "reflex/experimental", "reflex/__init__.py"],
1352
+ help="Target directories/files to process",
1353
+ )
1354
+ args = parser.parse_args()
1355
+
1340
1356
  logging.basicConfig(level=logging.INFO)
1341
1357
  logging.getLogger("blib2to3.pgen2.driver").setLevel(logging.INFO)
1342
1358
 
1343
1359
  gen = PyiGenerator()
1344
- gen.scan_all(
1345
- ["reflex/components", "reflex/experimental", "reflex/__init__.py"],
1346
- None,
1347
- use_json=True,
1348
- )
1360
+ gen.scan_all(args.targets, None, use_json=True)
@@ -9,7 +9,7 @@ import functools
9
9
  import inspect
10
10
  import json
11
11
  import warnings
12
- from collections.abc import Callable, Sequence
12
+ from collections.abc import Callable, Mapping, Sequence
13
13
  from datetime import date, datetime, time, timedelta
14
14
  from enum import Enum
15
15
  from pathlib import Path
@@ -335,6 +335,19 @@ def serialize_sequence(value: Sequence) -> list:
335
335
  return list(value)
336
336
 
337
337
 
338
+ @serializer(to=dict)
339
+ def serialize_mapping(value: Mapping) -> dict:
340
+ """Serialize a mapping type to a dictionary.
341
+
342
+ Args:
343
+ value: The mapping instance to serialize.
344
+
345
+ Returns:
346
+ A new dictionary containing the same key-value pairs as the input mapping.
347
+ """
348
+ return {**value}
349
+
350
+
338
351
  @serializer(to=str)
339
352
  def serialize_datetime(dt: date | datetime | time | timedelta) -> str:
340
353
  """Serialize a datetime to a JSON string.
reflex/utils/types.py CHANGED
@@ -3,10 +3,10 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import dataclasses
6
- import inspect
6
+ import sys
7
7
  import types
8
8
  from collections.abc import Callable, Iterable, Mapping, Sequence
9
- from functools import cached_property, lru_cache, wraps
9
+ from functools import cached_property, lru_cache
10
10
  from types import GenericAlias
11
11
  from typing import ( # noqa: UP035
12
12
  TYPE_CHECKING,
@@ -19,8 +19,11 @@ from typing import ( # noqa: UP035
19
19
  Literal,
20
20
  MutableMapping,
21
21
  NoReturn,
22
+ Protocol,
22
23
  Tuple,
24
+ TypeVar,
23
25
  Union,
26
+ _eval_type, # pyright: ignore [reportAttributeAccessIssue]
24
27
  _GenericAlias, # pyright: ignore [reportAttributeAccessIssue]
25
28
  _SpecialGenericAlias, # pyright: ignore [reportAttributeAccessIssue]
26
29
  get_args,
@@ -58,18 +61,91 @@ StateIterVar = list | set | tuple
58
61
  if TYPE_CHECKING:
59
62
  from reflex.vars.base import Var
60
63
 
61
- ArgsSpec = (
62
- Callable[[], Sequence[Var]]
63
- | Callable[[Var], Sequence[Var]]
64
- | Callable[[Var, Var], Sequence[Var]]
65
- | Callable[[Var, Var, Var], Sequence[Var]]
66
- | Callable[[Var, Var, Var, Var], Sequence[Var]]
67
- | Callable[[Var, Var, Var, Var, Var], Sequence[Var]]
68
- | Callable[[Var, Var, Var, Var, Var, Var], Sequence[Var]]
69
- | Callable[[Var, Var, Var, Var, Var, Var, Var], Sequence[Var]]
70
- )
71
- else:
72
- ArgsSpec = Callable[..., list[Any]]
64
+ VAR1 = TypeVar("VAR1", bound="Var")
65
+ VAR2 = TypeVar("VAR2", bound="Var")
66
+ VAR3 = TypeVar("VAR3", bound="Var")
67
+ VAR4 = TypeVar("VAR4", bound="Var")
68
+ VAR5 = TypeVar("VAR5", bound="Var")
69
+ VAR6 = TypeVar("VAR6", bound="Var")
70
+ VAR7 = TypeVar("VAR7", bound="Var")
71
+
72
+
73
+ class _ArgsSpec0(Protocol):
74
+ def __call__(self) -> Sequence[Var]: ...
75
+
76
+
77
+ class _ArgsSpec1(Protocol):
78
+ def __call__(self, var1: VAR1, /) -> Sequence[Var]: ... # pyright: ignore [reportInvalidTypeVarUse]
79
+
80
+
81
+ class _ArgsSpec2(Protocol):
82
+ def __call__(self, var1: VAR1, var2: VAR2, /) -> Sequence[Var]: ... # pyright: ignore [reportInvalidTypeVarUse]
83
+
84
+
85
+ class _ArgsSpec3(Protocol):
86
+ def __call__(self, var1: VAR1, var2: VAR2, var3: VAR3, /) -> Sequence[Var]: ... # pyright: ignore [reportInvalidTypeVarUse]
87
+
88
+
89
+ class _ArgsSpec4(Protocol):
90
+ def __call__(
91
+ self,
92
+ var1: VAR1, # pyright: ignore [reportInvalidTypeVarUse]
93
+ var2: VAR2, # pyright: ignore [reportInvalidTypeVarUse]
94
+ var3: VAR3, # pyright: ignore [reportInvalidTypeVarUse]
95
+ var4: VAR4, # pyright: ignore [reportInvalidTypeVarUse]
96
+ /,
97
+ ) -> Sequence[Var]: ...
98
+
99
+
100
+ class _ArgsSpec5(Protocol):
101
+ def __call__(
102
+ self,
103
+ var1: VAR1, # pyright: ignore [reportInvalidTypeVarUse]
104
+ var2: VAR2, # pyright: ignore [reportInvalidTypeVarUse]
105
+ var3: VAR3, # pyright: ignore [reportInvalidTypeVarUse]
106
+ var4: VAR4, # pyright: ignore [reportInvalidTypeVarUse]
107
+ var5: VAR5, # pyright: ignore [reportInvalidTypeVarUse]
108
+ /,
109
+ ) -> Sequence[Var]: ...
110
+
111
+
112
+ class _ArgsSpec6(Protocol):
113
+ def __call__(
114
+ self,
115
+ var1: VAR1, # pyright: ignore [reportInvalidTypeVarUse]
116
+ var2: VAR2, # pyright: ignore [reportInvalidTypeVarUse]
117
+ var3: VAR3, # pyright: ignore [reportInvalidTypeVarUse]
118
+ var4: VAR4, # pyright: ignore [reportInvalidTypeVarUse]
119
+ var5: VAR5, # pyright: ignore [reportInvalidTypeVarUse]
120
+ var6: VAR6, # pyright: ignore [reportInvalidTypeVarUse]
121
+ /,
122
+ ) -> Sequence[Var]: ...
123
+
124
+
125
+ class _ArgsSpec7(Protocol):
126
+ def __call__(
127
+ self,
128
+ var1: VAR1, # pyright: ignore [reportInvalidTypeVarUse]
129
+ var2: VAR2, # pyright: ignore [reportInvalidTypeVarUse]
130
+ var3: VAR3, # pyright: ignore [reportInvalidTypeVarUse]
131
+ var4: VAR4, # pyright: ignore [reportInvalidTypeVarUse]
132
+ var5: VAR5, # pyright: ignore [reportInvalidTypeVarUse]
133
+ var6: VAR6, # pyright: ignore [reportInvalidTypeVarUse]
134
+ var7: VAR7, # pyright: ignore [reportInvalidTypeVarUse]
135
+ /,
136
+ ) -> Sequence[Var]: ...
137
+
138
+
139
+ ArgsSpec = (
140
+ _ArgsSpec0
141
+ | _ArgsSpec1
142
+ | _ArgsSpec2
143
+ | _ArgsSpec3
144
+ | _ArgsSpec4
145
+ | _ArgsSpec5
146
+ | _ArgsSpec6
147
+ | _ArgsSpec7
148
+ )
73
149
 
74
150
  Scope = MutableMapping[str, Any]
75
151
  Message = MutableMapping[str, Any]
@@ -85,11 +161,7 @@ PrimitiveToAnnotation = {
85
161
  dict: Dict, # noqa: UP006
86
162
  }
87
163
 
88
- RESERVED_BACKEND_VAR_NAMES = {
89
- "_abc_impl",
90
- "_backend_vars",
91
- "_was_touched",
92
- }
164
+ RESERVED_BACKEND_VAR_NAMES = {"_abc_impl", "_backend_vars", "_was_touched", "_mixin"}
93
165
 
94
166
 
95
167
  class Unset:
@@ -251,7 +323,11 @@ def is_optional(cls: GenericType) -> bool:
251
323
  Returns:
252
324
  Whether the class is an Optional.
253
325
  """
254
- return is_union(cls) and type(None) in get_args(cls)
326
+ return (
327
+ cls is None
328
+ or cls is type(None)
329
+ or (is_union(cls) and type(None) in get_args(cls))
330
+ )
255
331
 
256
332
 
257
333
  def is_classvar(a_type: Any) -> bool:
@@ -917,47 +993,6 @@ def validate_literal(key: str, value: Any, expected_type: type, comp_name: str):
917
993
  raise ValueError(msg)
918
994
 
919
995
 
920
- def validate_parameter_literals(func: Callable):
921
- """Decorator to check that the arguments passed to a function
922
- correspond to the correct function parameter if it (the parameter)
923
- is a literal type.
924
-
925
- Args:
926
- func: The function to validate.
927
-
928
- Returns:
929
- The wrapper function.
930
- """
931
- console.deprecate(
932
- "validate_parameter_literals",
933
- reason="Use manual validation instead.",
934
- deprecation_version="0.7.11",
935
- removal_version="0.8.0",
936
- dedupe=True,
937
- )
938
-
939
- func_params = list(inspect.signature(func).parameters.items())
940
- annotations = {param[0]: param[1].annotation for param in func_params}
941
-
942
- @wraps(func)
943
- def wrapper(*args, **kwargs):
944
- # validate args
945
- for param, arg in zip(annotations, args, strict=False):
946
- if annotations[param] is inspect.Parameter.empty:
947
- continue
948
- validate_literal(param, arg, annotations[param], func.__name__)
949
-
950
- # validate kwargs.
951
- for key, value in kwargs.items():
952
- annotation = annotations.get(key)
953
- if not annotation or annotation is inspect.Parameter.empty:
954
- continue
955
- validate_literal(key, value, annotation, func.__name__)
956
- return func(*args, **kwargs)
957
-
958
- return wrapper
959
-
960
-
961
996
  # Store this here for performance.
962
997
  StateBases = get_base_class(StateVar)
963
998
  StateIterBases = get_base_class(StateIterVar)
@@ -1118,3 +1153,103 @@ def typehint_issubclass(
1118
1153
  )
1119
1154
  if accepted_arg is not Any
1120
1155
  )
1156
+
1157
+
1158
+ def resolve_annotations(
1159
+ raw_annotations: Mapping[str, type[Any]], module_name: str | None
1160
+ ) -> dict[str, type[Any]]:
1161
+ """Partially taken from typing.get_type_hints.
1162
+
1163
+ Resolve string or ForwardRef annotations into type objects if possible.
1164
+
1165
+ Args:
1166
+ raw_annotations: The raw annotations to resolve.
1167
+ module_name: The name of the module.
1168
+
1169
+ Returns:
1170
+ The resolved annotations.
1171
+ """
1172
+ module = sys.modules.get(module_name, None) if module_name is not None else None
1173
+
1174
+ base_globals: dict[str, Any] | None = (
1175
+ module.__dict__ if module is not None else None
1176
+ )
1177
+
1178
+ annotations = {}
1179
+ for name, value in raw_annotations.items():
1180
+ if isinstance(value, str):
1181
+ if sys.version_info == (3, 10, 0):
1182
+ value = ForwardRef(value, is_argument=False)
1183
+ else:
1184
+ value = ForwardRef(value, is_argument=False, is_class=True)
1185
+ try:
1186
+ if sys.version_info >= (3, 13):
1187
+ value = _eval_type(value, base_globals, None, type_params=())
1188
+ else:
1189
+ value = _eval_type(value, base_globals, None)
1190
+ except NameError:
1191
+ # this is ok, it can be fixed with update_forward_refs
1192
+ pass
1193
+ annotations[name] = value
1194
+ return annotations
1195
+
1196
+
1197
+ TYPES_THAT_HAS_DEFAULT_VALUE = (int, float, tuple, list, set, dict, str)
1198
+
1199
+
1200
+ def get_default_value_for_type(t: GenericType) -> Any:
1201
+ """Get the default value of the var.
1202
+
1203
+ Args:
1204
+ t: The type of the var.
1205
+
1206
+ Returns:
1207
+ The default value of the var, if it has one, else None.
1208
+
1209
+ Raises:
1210
+ ImportError: If the var is a dataframe and pandas is not installed.
1211
+ """
1212
+ if is_optional(t):
1213
+ return None
1214
+
1215
+ origin = get_origin(t) if is_generic_alias(t) else t
1216
+ if origin is Literal:
1217
+ args = get_args(t)
1218
+ return args[0] if args else None
1219
+ if safe_issubclass(origin, TYPES_THAT_HAS_DEFAULT_VALUE):
1220
+ return origin()
1221
+ if safe_issubclass(origin, Mapping):
1222
+ return {}
1223
+ if is_dataframe(origin):
1224
+ try:
1225
+ import pandas as pd
1226
+
1227
+ return pd.DataFrame()
1228
+ except ImportError as e:
1229
+ msg = "Please install pandas to use dataframes in your app."
1230
+ raise ImportError(msg) from e
1231
+ return None
1232
+
1233
+
1234
+ IMMUTABLE_TYPES = (
1235
+ int,
1236
+ float,
1237
+ bool,
1238
+ str,
1239
+ bytes,
1240
+ frozenset,
1241
+ tuple,
1242
+ type(None),
1243
+ )
1244
+
1245
+
1246
+ def is_immutable(i: Any) -> bool:
1247
+ """Check if a value is immutable.
1248
+
1249
+ Args:
1250
+ i: The value to check.
1251
+
1252
+ Returns:
1253
+ Whether the value is immutable.
1254
+ """
1255
+ return isinstance(i, IMMUTABLE_TYPES)
reflex/vars/__init__.py CHANGED
@@ -1,5 +1,7 @@
1
1
  """Immutable-Based Var System."""
2
2
 
3
+ from .base import BaseStateMeta as BaseStateMeta
4
+ from .base import EvenMoreBasicBaseState as EvenMoreBasicBaseState
3
5
  from .base import Field as Field
4
6
  from .base import LiteralVar as LiteralVar
5
7
  from .base import Var as Var