reflex 0.7.13a2__py3-none-any.whl → 0.7.14a1__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 (256) hide show
  1. reflex/.templates/apps/blank/code/blank.py +0 -2
  2. reflex/app.py +64 -69
  3. reflex/app_mixins/lifespan.py +2 -3
  4. reflex/app_mixins/middleware.py +1 -0
  5. reflex/app_mixins/mixin.py +0 -1
  6. reflex/assets.py +6 -3
  7. reflex/base.py +3 -2
  8. reflex/compiler/compiler.py +77 -64
  9. reflex/compiler/utils.py +8 -6
  10. reflex/components/base/app_wrap.pyi +0 -1
  11. reflex/components/base/bare.py +5 -7
  12. reflex/components/base/body.pyi +0 -1
  13. reflex/components/base/document.pyi +0 -5
  14. reflex/components/base/error_boundary.pyi +0 -1
  15. reflex/components/base/fragment.pyi +0 -1
  16. reflex/components/base/head.pyi +0 -2
  17. reflex/components/base/link.pyi +0 -2
  18. reflex/components/base/meta.py +2 -1
  19. reflex/components/base/meta.pyi +0 -4
  20. reflex/components/base/script.py +2 -1
  21. reflex/components/base/script.pyi +0 -1
  22. reflex/components/base/strict_mode.pyi +0 -1
  23. reflex/components/component.py +38 -40
  24. reflex/components/core/auto_scroll.pyi +0 -1
  25. reflex/components/core/banner.pyi +0 -6
  26. reflex/components/core/breakpoints.py +9 -11
  27. reflex/components/core/client_side_routing.pyi +0 -2
  28. reflex/components/core/clipboard.pyi +0 -1
  29. reflex/components/core/colors.py +10 -7
  30. reflex/components/core/cond.py +4 -2
  31. reflex/components/core/debounce.py +5 -3
  32. reflex/components/core/debounce.pyi +0 -1
  33. reflex/components/core/foreach.py +8 -6
  34. reflex/components/core/html.py +3 -3
  35. reflex/components/core/html.pyi +0 -1
  36. reflex/components/core/match.py +19 -17
  37. reflex/components/core/sticky.pyi +0 -4
  38. reflex/components/core/upload.pyi +0 -5
  39. reflex/components/datadisplay/code.py +1 -2
  40. reflex/components/datadisplay/code.pyi +0 -2
  41. reflex/components/datadisplay/dataeditor.py +7 -10
  42. reflex/components/datadisplay/dataeditor.pyi +0 -1
  43. reflex/components/datadisplay/logo.py +3 -4
  44. reflex/components/datadisplay/shiki_code_block.py +8 -11
  45. reflex/components/datadisplay/shiki_code_block.pyi +0 -3
  46. reflex/components/dynamic.py +2 -3
  47. reflex/components/el/__init__.pyi +2 -0
  48. reflex/components/el/element.pyi +0 -1
  49. reflex/components/el/elements/__init__.py +1 -0
  50. reflex/components/el/elements/__init__.pyi +3 -0
  51. reflex/components/el/elements/base.pyi +0 -1
  52. reflex/components/el/elements/forms.py +3 -4
  53. reflex/components/el/elements/forms.pyi +1 -18
  54. reflex/components/el/elements/inline.pyi +0 -28
  55. reflex/components/el/elements/media.py +26 -0
  56. reflex/components/el/elements/media.pyi +259 -25
  57. reflex/components/el/elements/metadata.py +0 -1
  58. reflex/components/el/elements/metadata.pyi +0 -6
  59. reflex/components/el/elements/other.pyi +0 -7
  60. reflex/components/el/elements/scripts.pyi +0 -3
  61. reflex/components/el/elements/sectioning.pyi +0 -15
  62. reflex/components/el/elements/tables.pyi +0 -10
  63. reflex/components/el/elements/typography.pyi +0 -15
  64. reflex/components/gridjs/datatable.py +10 -13
  65. reflex/components/gridjs/datatable.pyi +0 -2
  66. reflex/components/lucide/icon.py +10 -9
  67. reflex/components/lucide/icon.pyi +0 -3
  68. reflex/components/markdown/markdown.py +6 -8
  69. reflex/components/markdown/markdown.pyi +0 -1
  70. reflex/components/moment/moment.pyi +0 -1
  71. reflex/components/next/base.py +0 -2
  72. reflex/components/next/base.pyi +0 -3
  73. reflex/components/next/image.pyi +0 -1
  74. reflex/components/next/link.pyi +0 -1
  75. reflex/components/next/video.pyi +0 -1
  76. reflex/components/plotly/plotly.pyi +0 -9
  77. reflex/components/props.py +4 -3
  78. reflex/components/radix/primitives/accordion.pyi +0 -7
  79. reflex/components/radix/primitives/base.py +1 -3
  80. reflex/components/radix/primitives/base.pyi +0 -2
  81. reflex/components/radix/primitives/drawer.pyi +0 -11
  82. reflex/components/radix/primitives/form.py +4 -8
  83. reflex/components/radix/primitives/form.pyi +0 -12
  84. reflex/components/radix/primitives/progress.py +1 -1
  85. reflex/components/radix/primitives/progress.pyi +0 -5
  86. reflex/components/radix/primitives/slider.py +1 -1
  87. reflex/components/radix/primitives/slider.pyi +0 -5
  88. reflex/components/radix/themes/base.pyi +0 -8
  89. reflex/components/radix/themes/color_mode.pyi +0 -3
  90. reflex/components/radix/themes/components/alert_dialog.py +4 -2
  91. reflex/components/radix/themes/components/alert_dialog.pyi +4 -9
  92. reflex/components/radix/themes/components/aspect_ratio.py +1 -2
  93. reflex/components/radix/themes/components/aspect_ratio.pyi +1 -3
  94. reflex/components/radix/themes/components/avatar.py +5 -2
  95. reflex/components/radix/themes/components/avatar.pyi +1 -3
  96. reflex/components/radix/themes/components/badge.py +5 -2
  97. reflex/components/radix/themes/components/badge.pyi +1 -3
  98. reflex/components/radix/themes/components/button.py +2 -3
  99. reflex/components/radix/themes/components/button.pyi +1 -3
  100. reflex/components/radix/themes/components/callout.py +1 -2
  101. reflex/components/radix/themes/components/callout.pyi +1 -7
  102. reflex/components/radix/themes/components/card.py +1 -2
  103. reflex/components/radix/themes/components/card.pyi +1 -3
  104. reflex/components/radix/themes/components/checkbox.py +7 -4
  105. reflex/components/radix/themes/components/checkbox.pyi +1 -5
  106. reflex/components/radix/themes/components/checkbox_cards.py +1 -2
  107. reflex/components/radix/themes/components/checkbox_cards.pyi +1 -4
  108. reflex/components/radix/themes/components/checkbox_group.py +1 -2
  109. reflex/components/radix/themes/components/checkbox_group.pyi +1 -4
  110. reflex/components/radix/themes/components/context_menu.py +1 -1
  111. reflex/components/radix/themes/components/context_menu.pyi +1 -14
  112. reflex/components/radix/themes/components/data_list.py +1 -2
  113. reflex/components/radix/themes/components/data_list.pyi +1 -6
  114. reflex/components/radix/themes/components/dialog.py +4 -2
  115. reflex/components/radix/themes/components/dialog.pyi +4 -9
  116. reflex/components/radix/themes/components/dropdown_menu.py +5 -2
  117. reflex/components/radix/themes/components/dropdown_menu.pyi +4 -10
  118. reflex/components/radix/themes/components/hover_card.py +4 -2
  119. reflex/components/radix/themes/components/hover_card.pyi +4 -6
  120. reflex/components/radix/themes/components/icon_button.py +7 -8
  121. reflex/components/radix/themes/components/icon_button.pyi +1 -3
  122. reflex/components/radix/themes/components/inset.py +1 -2
  123. reflex/components/radix/themes/components/inset.pyi +1 -3
  124. reflex/components/radix/themes/components/popover.py +4 -2
  125. reflex/components/radix/themes/components/popover.pyi +4 -6
  126. reflex/components/radix/themes/components/progress.py +1 -2
  127. reflex/components/radix/themes/components/progress.pyi +1 -3
  128. reflex/components/radix/themes/components/radio.py +1 -2
  129. reflex/components/radix/themes/components/radio.pyi +1 -3
  130. reflex/components/radix/themes/components/radio_cards.py +1 -2
  131. reflex/components/radix/themes/components/radio_cards.pyi +1 -4
  132. reflex/components/radix/themes/components/radio_group.py +7 -5
  133. reflex/components/radix/themes/components/radio_group.pyi +1 -6
  134. reflex/components/radix/themes/components/scroll_area.py +1 -2
  135. reflex/components/radix/themes/components/scroll_area.pyi +1 -3
  136. reflex/components/radix/themes/components/segmented_control.py +1 -2
  137. reflex/components/radix/themes/components/segmented_control.pyi +1 -4
  138. reflex/components/radix/themes/components/select.py +5 -2
  139. reflex/components/radix/themes/components/select.pyi +1 -11
  140. reflex/components/radix/themes/components/separator.py +1 -2
  141. reflex/components/radix/themes/components/separator.pyi +1 -3
  142. reflex/components/radix/themes/components/skeleton.py +1 -2
  143. reflex/components/radix/themes/components/skeleton.pyi +1 -3
  144. reflex/components/radix/themes/components/slider.py +1 -2
  145. reflex/components/radix/themes/components/slider.pyi +1 -3
  146. reflex/components/radix/themes/components/spinner.py +1 -2
  147. reflex/components/radix/themes/components/spinner.pyi +1 -3
  148. reflex/components/radix/themes/components/switch.py +1 -2
  149. reflex/components/radix/themes/components/switch.pyi +1 -3
  150. reflex/components/radix/themes/components/table.py +1 -2
  151. reflex/components/radix/themes/components/table.pyi +1 -9
  152. reflex/components/radix/themes/components/tabs.py +1 -2
  153. reflex/components/radix/themes/components/tabs.pyi +1 -7
  154. reflex/components/radix/themes/components/text_area.py +5 -2
  155. reflex/components/radix/themes/components/text_area.pyi +1 -3
  156. reflex/components/radix/themes/components/text_field.py +5 -2
  157. reflex/components/radix/themes/components/text_field.pyi +1 -5
  158. reflex/components/radix/themes/components/tooltip.py +1 -2
  159. reflex/components/radix/themes/components/tooltip.pyi +1 -3
  160. reflex/components/radix/themes/layout/base.py +5 -2
  161. reflex/components/radix/themes/layout/base.pyi +5 -3
  162. reflex/components/radix/themes/layout/box.py +1 -2
  163. reflex/components/radix/themes/layout/box.pyi +1 -3
  164. reflex/components/radix/themes/layout/center.pyi +0 -1
  165. reflex/components/radix/themes/layout/container.py +1 -2
  166. reflex/components/radix/themes/layout/container.pyi +1 -3
  167. reflex/components/radix/themes/layout/flex.py +6 -2
  168. reflex/components/radix/themes/layout/flex.pyi +1 -3
  169. reflex/components/radix/themes/layout/grid.py +6 -2
  170. reflex/components/radix/themes/layout/grid.pyi +1 -3
  171. reflex/components/radix/themes/layout/list.py +2 -1
  172. reflex/components/radix/themes/layout/list.pyi +0 -5
  173. reflex/components/radix/themes/layout/section.py +1 -2
  174. reflex/components/radix/themes/layout/section.pyi +1 -3
  175. reflex/components/radix/themes/layout/spacer.pyi +0 -1
  176. reflex/components/radix/themes/layout/stack.py +1 -1
  177. reflex/components/radix/themes/layout/stack.pyi +0 -3
  178. reflex/components/radix/themes/typography/blockquote.py +1 -1
  179. reflex/components/radix/themes/typography/blockquote.pyi +1 -3
  180. reflex/components/radix/themes/typography/code.py +5 -1
  181. reflex/components/radix/themes/typography/code.pyi +1 -3
  182. reflex/components/radix/themes/typography/heading.py +1 -1
  183. reflex/components/radix/themes/typography/heading.pyi +1 -3
  184. reflex/components/radix/themes/typography/link.py +3 -2
  185. reflex/components/radix/themes/typography/link.pyi +1 -3
  186. reflex/components/radix/themes/typography/text.py +1 -1
  187. reflex/components/radix/themes/typography/text.pyi +1 -9
  188. reflex/components/react_player/audio.py +0 -2
  189. reflex/components/react_player/audio.pyi +0 -3
  190. reflex/components/react_player/react_player.pyi +0 -1
  191. reflex/components/react_player/video.py +0 -2
  192. reflex/components/react_player/video.pyi +0 -3
  193. reflex/components/recharts/__init__.py +1 -1
  194. reflex/components/recharts/__init__.pyi +1 -1
  195. reflex/components/recharts/cartesian.py +20 -25
  196. reflex/components/recharts/cartesian.pyi +20 -37
  197. reflex/components/recharts/charts.py +2 -1
  198. reflex/components/recharts/charts.pyi +0 -12
  199. reflex/components/recharts/general.pyi +0 -6
  200. reflex/components/recharts/polar.py +5 -4
  201. reflex/components/recharts/polar.pyi +4 -10
  202. reflex/components/recharts/recharts.py +12 -10
  203. reflex/components/recharts/recharts.pyi +10 -11
  204. reflex/components/sonner/toast.py +2 -2
  205. reflex/components/sonner/toast.pyi +0 -2
  206. reflex/components/suneditor/editor.py +2 -1
  207. reflex/components/suneditor/editor.pyi +0 -1
  208. reflex/components/tags/iter_tag.py +4 -2
  209. reflex/config.py +47 -35
  210. reflex/constants/base.py +3 -3
  211. reflex/constants/compiler.py +8 -6
  212. reflex/constants/installer.py +24 -15
  213. reflex/custom_components/custom_components.py +1 -2
  214. reflex/event.py +58 -60
  215. reflex/experimental/__init__.py +2 -2
  216. reflex/experimental/client_state.py +9 -4
  217. reflex/experimental/layout.pyi +0 -5
  218. reflex/istate/manager.py +15 -19
  219. reflex/istate/proxy.py +19 -12
  220. reflex/model.py +6 -4
  221. reflex/plugins/base.py +8 -0
  222. reflex/plugins/tailwind_v3.py +8 -0
  223. reflex/plugins/tailwind_v4.py +8 -0
  224. reflex/reflex.py +9 -11
  225. reflex/route.py +7 -9
  226. reflex/state.py +66 -70
  227. reflex/style.py +3 -1
  228. reflex/testing.py +46 -29
  229. reflex/utils/build.py +2 -1
  230. reflex/utils/console.py +9 -17
  231. reflex/utils/exec.py +9 -11
  232. reflex/utils/format.py +21 -24
  233. reflex/utils/imports.py +4 -3
  234. reflex/utils/lazy_loader.py +3 -3
  235. reflex/utils/misc.py +2 -1
  236. reflex/utils/net.py +2 -2
  237. reflex/utils/path_ops.py +2 -1
  238. reflex/utils/prerequisites.py +67 -38
  239. reflex/utils/processes.py +4 -6
  240. reflex/utils/pyi_generator.py +46 -41
  241. reflex/utils/redir.py +1 -1
  242. reflex/utils/serializers.py +4 -4
  243. reflex/utils/telemetry.py +42 -4
  244. reflex/utils/types.py +16 -13
  245. reflex/vars/base.py +96 -109
  246. reflex/vars/datetime.py +2 -1
  247. reflex/vars/dep_tracking.py +19 -28
  248. reflex/vars/number.py +6 -7
  249. reflex/vars/object.py +5 -6
  250. reflex/vars/sequence.py +11 -11
  251. {reflex-0.7.13a2.dist-info → reflex-0.7.14a1.dist-info}/METADATA +1 -1
  252. reflex-0.7.14a1.dist-info/RECORD +407 -0
  253. reflex-0.7.13a2.dist-info/RECORD +0 -407
  254. {reflex-0.7.13a2.dist-info → reflex-0.7.14a1.dist-info}/WHEEL +0 -0
  255. {reflex-0.7.13a2.dist-info → reflex-0.7.14a1.dist-info}/entry_points.txt +0 -0
  256. {reflex-0.7.13a2.dist-info → reflex-0.7.14a1.dist-info}/licenses/LICENSE +0 -0
@@ -129,7 +129,6 @@ def check_latest_package_version(package_name: str):
129
129
  )
130
130
  except Exception:
131
131
  console.debug(f"Failed to check for the latest version of {package_name}.")
132
- pass
133
132
 
134
133
 
135
134
  def get_or_set_last_reflex_version_check_datetime():
@@ -255,9 +254,8 @@ def get_nodejs_compatible_package_managers(
255
254
  package_managers = list(filter(None, package_managers))
256
255
 
257
256
  if not package_managers and raise_on_none:
258
- raise FileNotFoundError(
259
- "Bun or npm not found. You might need to rerun `reflex init` or install either."
260
- )
257
+ msg = "Bun or npm not found. You might need to rerun `reflex init` or install either."
258
+ raise FileNotFoundError(msg)
261
259
 
262
260
  return package_managers
263
261
 
@@ -310,9 +308,8 @@ def get_js_package_executor(raise_on_none: bool = False) -> Sequence[Sequence[st
310
308
  package_managers = list(filter(None, package_managers))
311
309
 
312
310
  if not package_managers and raise_on_none:
313
- raise FileNotFoundError(
314
- "Bun or npm not found. You might need to rerun `reflex init` or install either."
315
- )
311
+ msg = "Bun or npm not found. You might need to rerun `reflex init` or install either."
312
+ raise FileNotFoundError(msg)
316
313
 
317
314
  return package_managers
318
315
 
@@ -345,10 +342,11 @@ def _check_app_name(config: Config):
345
342
  RuntimeError: If the app name is not set in the config.
346
343
  """
347
344
  if not config.app_name:
348
- raise RuntimeError(
345
+ msg = (
349
346
  "Cannot get the app module because `app_name` is not set in rxconfig! "
350
347
  "If this error occurs in a reflex test case, ensure that `get_app` is mocked."
351
348
  )
349
+ raise RuntimeError(msg)
352
350
 
353
351
 
354
352
  def get_app(reload: bool = False) -> ModuleType:
@@ -395,11 +393,14 @@ def get_app(reload: bool = False) -> ModuleType:
395
393
  return app
396
394
 
397
395
 
398
- def get_and_validate_app(reload: bool = False) -> AppInfo:
396
+ def get_and_validate_app(
397
+ reload: bool = False, check_if_schema_up_to_date: bool = False
398
+ ) -> AppInfo:
399
399
  """Get the app instance based on the default config and validate it.
400
400
 
401
401
  Args:
402
402
  reload: Re-import the app module from disk
403
+ check_if_schema_up_to_date: If True, check if the schema is up to date.
403
404
 
404
405
  Returns:
405
406
  The app instance and the app module.
@@ -412,23 +413,34 @@ def get_and_validate_app(reload: bool = False) -> AppInfo:
412
413
  app_module = get_app(reload=reload)
413
414
  app = getattr(app_module, constants.CompileVars.APP)
414
415
  if not isinstance(app, App):
415
- raise RuntimeError(
416
- "The app instance in the specified app_module_import in rxconfig must be an instance of rx.App."
417
- )
416
+ msg = "The app instance in the specified app_module_import in rxconfig must be an instance of rx.App."
417
+ raise RuntimeError(msg)
418
+
419
+ if check_if_schema_up_to_date:
420
+ check_schema_up_to_date()
421
+
418
422
  return AppInfo(app=app, module=app_module)
419
423
 
420
424
 
421
- def validate_app(reload: bool = False) -> None:
425
+ def validate_app(
426
+ reload: bool = False, check_if_schema_up_to_date: bool = False
427
+ ) -> None:
422
428
  """Validate the app instance based on the default config.
423
429
 
424
430
  Args:
425
431
  reload: Re-import the app module from disk
432
+ check_if_schema_up_to_date: If True, check if the schema is up to date.
426
433
  """
427
- get_and_validate_app(reload=reload)
434
+ get_and_validate_app(
435
+ reload=reload, check_if_schema_up_to_date=check_if_schema_up_to_date
436
+ )
428
437
 
429
438
 
430
439
  def get_compiled_app(
431
- reload: bool = False, export: bool = False, dry_run: bool = False
440
+ reload: bool = False,
441
+ export: bool = False,
442
+ dry_run: bool = False,
443
+ check_if_schema_up_to_date: bool = False,
432
444
  ) -> ModuleType:
433
445
  """Get the app module based on the default config after first compiling it.
434
446
 
@@ -436,11 +448,14 @@ def get_compiled_app(
436
448
  reload: Re-import the app module from disk
437
449
  export: Compile the app for export
438
450
  dry_run: If True, do not write the compiled app to disk.
451
+ check_if_schema_up_to_date: If True, check if the schema is up to date.
439
452
 
440
453
  Returns:
441
454
  The compiled app based on the default config.
442
455
  """
443
- app, app_module = get_and_validate_app(reload=reload)
456
+ app, app_module = get_and_validate_app(
457
+ reload=reload, check_if_schema_up_to_date=check_if_schema_up_to_date
458
+ )
444
459
  # For py3.9 compatibility when redis is used, we MUST add any decorator pages
445
460
  # before compiling the app in a thread to avoid event loop error (REF-2172).
446
461
  app._apply_decorated_pages()
@@ -449,7 +464,10 @@ def get_compiled_app(
449
464
 
450
465
 
451
466
  def compile_app(
452
- reload: bool = False, export: bool = False, dry_run: bool = False
467
+ reload: bool = False,
468
+ export: bool = False,
469
+ dry_run: bool = False,
470
+ check_if_schema_up_to_date: bool = False,
453
471
  ) -> None:
454
472
  """Compile the app module based on the default config.
455
473
 
@@ -457,8 +475,14 @@ def compile_app(
457
475
  reload: Re-import the app module from disk
458
476
  export: Compile the app for export
459
477
  dry_run: If True, do not write the compiled app to disk.
478
+ check_if_schema_up_to_date: If True, check if the schema is up to date.
460
479
  """
461
- get_compiled_app(reload=reload, export=export, dry_run=dry_run)
480
+ get_compiled_app(
481
+ reload=reload,
482
+ export=export,
483
+ dry_run=dry_run,
484
+ check_if_schema_up_to_date=check_if_schema_up_to_date,
485
+ )
462
486
 
463
487
 
464
488
  def _can_colorize() -> bool:
@@ -503,20 +527,23 @@ def _can_colorize() -> bool:
503
527
  return file.isatty()
504
528
 
505
529
 
506
- def compile_or_validate_app(compile: bool = False) -> bool:
530
+ def compile_or_validate_app(
531
+ compile: bool = False, check_if_schema_up_to_date: bool = False
532
+ ) -> bool:
507
533
  """Compile or validate the app module based on the default config.
508
534
 
509
535
  Args:
510
536
  compile: Whether to compile the app.
537
+ check_if_schema_up_to_date: If True, check if the schema is up to date.
511
538
 
512
539
  Returns:
513
540
  If the app is compiled successfully.
514
541
  """
515
542
  try:
516
543
  if compile:
517
- compile_app()
544
+ compile_app(check_if_schema_up_to_date=check_if_schema_up_to_date)
518
545
  else:
519
- validate_app()
546
+ validate_app(check_if_schema_up_to_date=check_if_schema_up_to_date)
520
547
  except Exception as e:
521
548
  if isinstance(e, click.exceptions.Exit):
522
549
  return False
@@ -575,9 +602,8 @@ def parse_redis_url() -> str | None:
575
602
  if not config.redis_url:
576
603
  return None
577
604
  if not config.redis_url.startswith(("redis://", "rediss://", "unix://")):
578
- raise ValueError(
579
- "REDIS_URL must start with 'redis://', 'rediss://', or 'unix://'."
580
- )
605
+ msg = "REDIS_URL must start with 'redis://', 'rediss://', or 'unix://'."
606
+ raise ValueError(msg)
581
607
  return config.redis_url
582
608
 
583
609
 
@@ -1157,15 +1183,16 @@ def download_and_run(url: str, *args, show_status: bool = False, **env):
1157
1183
  raise click.exceptions.Exit(1) from None
1158
1184
 
1159
1185
  # Save the script to a temporary file.
1160
- script = Path(tempfile.NamedTemporaryFile().name)
1186
+ with tempfile.NamedTemporaryFile() as tempfile_file:
1187
+ script = Path(tempfile_file.name)
1161
1188
 
1162
- script.write_text(response.text)
1189
+ script.write_text(response.text)
1163
1190
 
1164
- # Run the script.
1165
- env = {**os.environ, **env}
1166
- process = processes.new_process(["bash", str(script), *args], env=env)
1167
- show = processes.show_status if show_status else processes.show_logs
1168
- show(f"Installing {url}", process)
1191
+ # Run the script.
1192
+ env = {**os.environ, **env}
1193
+ process = processes.new_process(["bash", str(script), *args], env=env)
1194
+ show = processes.show_status if show_status else processes.show_logs
1195
+ show(f"Installing {url}", process)
1169
1196
 
1170
1197
 
1171
1198
  def install_bun():
@@ -1213,7 +1240,8 @@ def install_bun():
1213
1240
  )
1214
1241
  else:
1215
1242
  if path_ops.which("unzip") is None:
1216
- raise SystemPackageMissingError("unzip")
1243
+ msg = "unzip"
1244
+ raise SystemPackageMissingError(msg)
1217
1245
 
1218
1246
  # Run the bun install script.
1219
1247
  download_and_run(
@@ -1262,13 +1290,15 @@ def cached_procedure(
1262
1290
  ValueError: If both cache_file and cache_file_fn are provided.
1263
1291
  """
1264
1292
  if cache_file and cache_file_fn is not None:
1265
- raise ValueError("cache_file and cache_file_fn cannot both be provided.")
1293
+ msg = "cache_file and cache_file_fn cannot both be provided."
1294
+ raise ValueError(msg)
1266
1295
 
1267
1296
  def _inner_decorator(func: Callable):
1268
1297
  def _inner(*args, **kwargs):
1269
1298
  _cache_file = cache_file_fn() if cache_file_fn is not None else cache_file
1270
1299
  if not _cache_file:
1271
- raise ValueError("Unknown cache file, cannot cache result.")
1300
+ msg = "Unknown cache file, cannot cache result."
1301
+ raise ValueError(msg)
1272
1302
  payload = _read_cached_procedure_file(_cache_file)
1273
1303
  new_payload = payload_fn(*args, **kwargs)
1274
1304
  if payload != new_payload:
@@ -1446,7 +1476,7 @@ def validate_bun(bun_path: Path | None = None):
1446
1476
  "Failed to obtain bun version. Make sure the specified bun path in your config is correct."
1447
1477
  )
1448
1478
  raise click.exceptions.Exit(1)
1449
- elif bun_version < version.parse(constants.Bun.MIN_VERSION):
1479
+ if bun_version < version.parse(constants.Bun.MIN_VERSION):
1450
1480
  console.warn(
1451
1481
  f"Reflex requires bun version {constants.Bun.MIN_VERSION} or higher to run, but the detected version is "
1452
1482
  f"{bun_version}. If you have specified a custom bun path in your config, make sure to provide one "
@@ -1657,8 +1687,7 @@ def fetch_app_templates(version: str) -> dict[str, Template]:
1657
1687
  if asset is None:
1658
1688
  console.warn(f"Templates metadata not found for version {version}")
1659
1689
  return {}
1660
- else:
1661
- templates_url = asset["browser_download_url"]
1690
+ templates_url = asset["browser_download_url"]
1662
1691
 
1663
1692
  templates_data = net.get(templates_url, follow_redirects=True).json()["templates"]
1664
1693
 
@@ -1864,7 +1893,7 @@ def initialize_app(app_name: str, template: str | None = None) -> str | None:
1864
1893
  # Check if the app is already initialized.
1865
1894
  if constants.Config.FILE.exists():
1866
1895
  telemetry.send("reinit")
1867
- return
1896
+ return None
1868
1897
 
1869
1898
  templates: dict[str, Template] = {}
1870
1899
 
reflex/utils/processes.py CHANGED
@@ -137,11 +137,10 @@ def handle_port(service_name: str, port: int, auto_increment: bool) -> int:
137
137
  return port
138
138
  if auto_increment:
139
139
  return change_port(port, service_name)
140
- else:
141
- console.error(
142
- f"{service_name.capitalize()} port: {port} is already in use by PID: {process.pid}."
143
- )
144
- raise click.exceptions.Exit()
140
+ console.error(
141
+ f"{service_name.capitalize()} port: {port} is already in use by PID: {process.pid}."
142
+ )
143
+ raise click.exceptions.Exit
145
144
 
146
145
 
147
146
  @overload
@@ -316,7 +315,6 @@ def stream_logs(
316
315
  # But if the process is still running that is weird.
317
316
  raise
318
317
  # If the process exited, break out of the loop for post processing.
319
- pass
320
318
 
321
319
  # Check if the process failed (not printing the logs for SIGINT).
322
320
 
@@ -30,6 +30,8 @@ logger = logging.getLogger("pyi_generator")
30
30
 
31
31
  PWD = Path.cwd()
32
32
 
33
+ PYI_HASHES = "pyi_hashes.json"
34
+
33
35
  EXCLUDED_FILES = [
34
36
  "app.py",
35
37
  "component.py",
@@ -157,9 +159,8 @@ def _get_type_hint(
157
159
  res_args.sort()
158
160
  if len(res_args) == 1:
159
161
  return f"{res_args[0]} | None"
160
- else:
161
- res = f"{' | '.join(res_args)}"
162
- return f"{res} | None"
162
+ res = f"{' | '.join(res_args)}"
163
+ return f"{res} | None"
163
164
 
164
165
  res_args = [
165
166
  _get_type_hint(arg, type_hint_globals, rx_types.is_optional(arg))
@@ -183,10 +184,11 @@ def _get_type_hint(
183
184
  value.__module__ not in ["builtins", "__builtins__"]
184
185
  and value.__name__ not in type_hint_globals
185
186
  ):
186
- raise TypeError(
187
+ msg = (
187
188
  f"{value.__module__ + '.' + value.__name__} is not a default import, "
188
189
  "add it to DEFAULT_IMPORTS in pyi_generator.py"
189
190
  )
191
+ raise TypeError(msg)
190
192
 
191
193
  res = f"{value.__name__}[{', '.join(inner_container_type_args)}]"
192
194
 
@@ -445,7 +447,7 @@ def type_to_ast(typ: Any, cls: type) -> ast.expr:
445
447
 
446
448
  return ast.Name(id=typ.__module__ + "." + typ.__name__)
447
449
  return ast.Name(id=typ.__name__)
448
- elif hasattr(typ, "_name"):
450
+ if hasattr(typ, "_name"):
449
451
  return ast.Name(id=typ._name)
450
452
  return ast.Name(id=str(typ))
451
453
 
@@ -510,7 +512,8 @@ def _generate_component_create_functiondef(
510
512
  TypeError: If clz is not a subclass of Component.
511
513
  """
512
514
  if not issubclass(clz, Component):
513
- raise TypeError(f"clz must be a subclass of Component, not {clz!r}")
515
+ msg = f"clz must be a subclass of Component, not {clz!r}"
516
+ raise TypeError(msg)
514
517
 
515
518
  # add the imports needed by get_type_hint later
516
519
  type_hint_globals.update(
@@ -654,7 +657,7 @@ def _generate_component_create_functiondef(
654
657
  defaults=[],
655
658
  )
656
659
 
657
- definition = ast.FunctionDef( # pyright: ignore [reportCallIssue]
660
+ return ast.FunctionDef( # pyright: ignore [reportCallIssue]
658
661
  name="create",
659
662
  args=create_args,
660
663
  body=[
@@ -676,7 +679,6 @@ def _generate_component_create_functiondef(
676
679
  lineno=lineno,
677
680
  returns=ast.Constant(value=clz.__name__),
678
681
  )
679
- return definition
680
682
 
681
683
 
682
684
  def _generate_staticmethod_call_functiondef(
@@ -710,7 +712,7 @@ def _generate_staticmethod_call_functiondef(
710
712
  else []
711
713
  ),
712
714
  )
713
- definition = ast.FunctionDef( # pyright: ignore [reportCallIssue]
715
+ return ast.FunctionDef( # pyright: ignore [reportCallIssue]
714
716
  name="__call__",
715
717
  args=call_args,
716
718
  body=[
@@ -729,7 +731,6 @@ def _generate_staticmethod_call_functiondef(
729
731
  )
730
732
  ),
731
733
  )
732
- return definition
733
734
 
734
735
 
735
736
  def _generate_namespace_call_functiondef(
@@ -841,6 +842,7 @@ class StubGenerator(ast.NodeTransformer):
841
842
  and issubclass((clz := self.classes[self.current_class]), Component)
842
843
  ):
843
844
  return clz
845
+ return None
844
846
 
845
847
  def visit_Module(self, node: ast.Module) -> ast.Module:
846
848
  """Visit a Module node and remove docstring from body.
@@ -1021,7 +1023,7 @@ class StubGenerator(ast.NodeTransformer):
1021
1023
  if isinstance(target, ast.Tuple):
1022
1024
  for name in target.elts:
1023
1025
  if isinstance(name, ast.Name) and name.id.startswith("_"):
1024
- return
1026
+ return None
1025
1027
 
1026
1028
  return node
1027
1029
 
@@ -1107,7 +1109,7 @@ class PyiGenerator:
1107
1109
  pyright_ignore_imports = getattr(mod, "_PYRIGHT_IGNORE_IMPORTS", [])
1108
1110
 
1109
1111
  if not sub_mods and not sub_mod_attrs:
1110
- return
1112
+ return None
1111
1113
  sub_mods_imports = []
1112
1114
  sub_mod_attrs_imports = []
1113
1115
 
@@ -1162,7 +1164,7 @@ class PyiGenerator:
1162
1164
  }
1163
1165
  is_init_file = _relative_to_pwd(module_path).name == "__init__.py"
1164
1166
  if not class_names and not is_init_file:
1165
- return
1167
+ return None
1166
1168
 
1167
1169
  if is_init_file:
1168
1170
  new_tree = InitStubGenerator(module, class_names).visit(
@@ -1170,7 +1172,7 @@ class PyiGenerator:
1170
1172
  )
1171
1173
  init_imports = self._get_init_lazy_imports(module, new_tree)
1172
1174
  if not init_imports:
1173
- return
1175
+ return None
1174
1176
  content_hash = self._write_pyi_file(module_path, init_imports)
1175
1177
  else:
1176
1178
  new_tree = StubGenerator(module, class_names).visit(
@@ -1264,45 +1266,48 @@ class PyiGenerator:
1264
1266
  file_parent = file_path.parent
1265
1267
  while len(file_parent.parts) > len(top_dir.parts):
1266
1268
  file_parent = file_parent.parent
1269
+ while len(top_dir.parts) > len(file_parent.parts):
1270
+ top_dir = top_dir.parent
1267
1271
  while not file_parent.samefile(top_dir):
1268
1272
  file_parent = file_parent.parent
1269
1273
  top_dir = top_dir.parent
1270
1274
 
1271
- pyi_hashes_file = top_dir / "pyi_hashes.json"
1272
- if not pyi_hashes_file.exists():
1273
- while top_dir.parent and not (top_dir / "pyi_hashes.json").exists():
1274
- top_dir = top_dir.parent
1275
- another_pyi_hashes_file = top_dir / "pyi_hashes.json"
1276
- if another_pyi_hashes_file.exists():
1277
- pyi_hashes_file = another_pyi_hashes_file
1278
-
1279
- pyi_hashes_file.write_text(
1280
- json.dumps(
1281
- dict(
1282
- zip(
1283
- [
1284
- f.relative_to(pyi_hashes_file.parent).as_posix()
1285
- for f in file_paths
1286
- ],
1287
- hashes,
1288
- strict=True,
1289
- )
1290
- ),
1291
- indent=2,
1292
- sort_keys=True,
1275
+ while (
1276
+ not top_dir.samefile(top_dir.parent)
1277
+ and not (top_dir / PYI_HASHES).exists()
1278
+ ):
1279
+ top_dir = top_dir.parent
1280
+
1281
+ pyi_hashes_file = top_dir / PYI_HASHES
1282
+
1283
+ if pyi_hashes_file.exists():
1284
+ pyi_hashes_file.write_text(
1285
+ json.dumps(
1286
+ dict(
1287
+ zip(
1288
+ [
1289
+ f.relative_to(pyi_hashes_file.parent).as_posix()
1290
+ for f in file_paths
1291
+ ],
1292
+ hashes,
1293
+ strict=True,
1294
+ )
1295
+ ),
1296
+ indent=2,
1297
+ sort_keys=True,
1298
+ )
1299
+ + "\n",
1293
1300
  )
1294
- + "\n",
1295
- )
1296
1301
  elif file_paths:
1297
1302
  file_paths = list(map(Path, file_paths))
1298
1303
  pyi_hashes_parent = file_paths[0].parent
1299
1304
  while (
1300
- pyi_hashes_parent.parent
1301
- and not (pyi_hashes_parent / "pyi_hashes.json").exists()
1305
+ not pyi_hashes_parent.samefile(pyi_hashes_parent.parent)
1306
+ and not (pyi_hashes_parent / PYI_HASHES).exists()
1302
1307
  ):
1303
1308
  pyi_hashes_parent = pyi_hashes_parent.parent
1304
1309
 
1305
- pyi_hashes_file = pyi_hashes_parent / "pyi_hashes.json"
1310
+ pyi_hashes_file = pyi_hashes_parent / PYI_HASHES
1306
1311
  if pyi_hashes_file.exists():
1307
1312
  pyi_hashes = json.loads(pyi_hashes_file.read_text())
1308
1313
  for file_path, hashed_content in zip(
reflex/utils/redir.py CHANGED
@@ -5,9 +5,9 @@ import webbrowser
5
5
 
6
6
  import httpx
7
7
 
8
+ from reflex import constants
8
9
  from reflex.utils import net
9
10
 
10
- from .. import constants
11
11
  from . import console
12
12
 
13
13
 
@@ -76,7 +76,8 @@ def serializer(
76
76
 
77
77
  # Make sure the function takes a single argument.
78
78
  if len(args) != 1:
79
- raise ValueError("Serializer must take a single argument.")
79
+ msg = "Serializer must take a single argument."
80
+ raise ValueError(msg)
80
81
 
81
82
  # Get the type of the argument.
82
83
  type_ = type_hints[args[0]]
@@ -166,8 +167,7 @@ def serialize(
166
167
  # Return the serialized value and the type.
167
168
  if get_type:
168
169
  return serialized, get_serializer_type(type(value))
169
- else:
170
- return serialized
170
+ return serialized
171
171
 
172
172
 
173
173
  @functools.lru_cache
@@ -427,7 +427,7 @@ with contextlib.suppress(ImportError):
427
427
  """
428
428
  return [
429
429
  [str(d) if isinstance(d, (list, tuple)) else d for d in data]
430
- for data in list(df.values.tolist())
430
+ for data in list(df.to_numpy().tolist())
431
431
  ]
432
432
 
433
433
  @serializer
reflex/utils/telemetry.py CHANGED
@@ -19,7 +19,12 @@ from reflex.config import environment
19
19
  from reflex.utils import console
20
20
  from reflex.utils.decorator import once_unless_none
21
21
  from reflex.utils.exceptions import ReflexError
22
- from reflex.utils.prerequisites import ensure_reflex_installation_id, get_project_hash
22
+ from reflex.utils.prerequisites import (
23
+ ensure_reflex_installation_id,
24
+ get_bun_version,
25
+ get_node_version,
26
+ get_project_hash,
27
+ )
23
28
 
24
29
  UTC = timezone.utc
25
30
  POSTHOG_API_URL: str = "https://app.posthog.com/capture/"
@@ -62,6 +67,22 @@ def get_reflex_version() -> str:
62
67
  return constants.Reflex.VERSION
63
68
 
64
69
 
70
+ def _get_app_class_name() -> str | None:
71
+ """Get the app class name if available.
72
+
73
+ Returns:
74
+ The app class name (e.g. "App", "AppEnterprise") or None if not available.
75
+ """
76
+ try:
77
+ from reflex.utils.prerequisites import get_and_validate_app
78
+
79
+ app_info = get_and_validate_app()
80
+ except Exception:
81
+ return None
82
+ else:
83
+ return app_info.app.__class__.__name__
84
+
85
+
65
86
  def get_cpu_count() -> int:
66
87
  """Get the number of CPUs.
67
88
 
@@ -106,6 +127,8 @@ class _Properties(TypedDict):
106
127
  user_os_detail: str
107
128
  reflex_version: str
108
129
  python_version: str
130
+ node_version: str | None
131
+ bun_version: str | None
109
132
  cpu_count: int
110
133
  memory: int
111
134
  cpu_info: dict
@@ -153,6 +176,12 @@ def _get_event_defaults() -> _DefaultEvent | None:
153
176
  "user_os_detail": get_detailed_platform_str(),
154
177
  "reflex_version": get_reflex_version(),
155
178
  "python_version": get_python_version(),
179
+ "node_version": (
180
+ str(node_version) if (node_version := get_node_version()) else None
181
+ ),
182
+ "bun_version": (
183
+ str(bun_version) if (bun_version := get_bun_version()) else None
184
+ ),
156
185
  "cpu_count": get_cpu_count(),
157
186
  "memory": get_memory(),
158
187
  "cpu_info": dataclasses.asdict(cpuinfo) if cpuinfo else {},
@@ -184,12 +213,16 @@ def _prepare_event(event: str, **kwargs) -> _Event | None:
184
213
  if not event_data:
185
214
  return None
186
215
 
187
- additional_keys = ["template", "context", "detail", "user_uuid"]
216
+ additional_keys = ["template", "context", "detail", "user_uuid", "app_class"]
188
217
 
189
218
  properties = event_data["properties"]
190
219
 
220
+ # Auto-detect app class if not provided
221
+ if "app_class" not in kwargs:
222
+ kwargs["app_class"] = _get_app_class_name()
223
+
191
224
  for key in additional_keys:
192
- if key in properties or key not in kwargs:
225
+ if key in properties or key not in kwargs or kwargs[key] is None:
193
226
  continue
194
227
 
195
228
  properties[key] = kwargs[key]
@@ -232,6 +265,9 @@ def _send(event: str, telemetry_enabled: bool | None, **kwargs) -> bool:
232
265
  return False
233
266
 
234
267
 
268
+ background_tasks = set()
269
+
270
+
235
271
  def send(event: str, telemetry_enabled: bool | None = None, **kwargs):
236
272
  """Send anonymous telemetry for Reflex.
237
273
 
@@ -246,7 +282,9 @@ def send(event: str, telemetry_enabled: bool | None = None, **kwargs):
246
282
 
247
283
  try:
248
284
  # Within an event loop context, send the event asynchronously.
249
- asyncio.create_task(async_send(event, telemetry_enabled, **kwargs))
285
+ task = asyncio.create_task(async_send(event, telemetry_enabled, **kwargs))
286
+ background_tasks.add(task)
287
+ task.add_done_callback(background_tasks.discard)
250
288
  except RuntimeError:
251
289
  # If there is no event loop, send the event synchronously.
252
290
  warnings.filterwarnings("ignore", category=RuntimeWarning)