reflex 0.6.8a1__py3-none-any.whl → 0.7.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 (155) hide show
  1. reflex/.templates/jinja/custom_components/pyproject.toml.jinja2 +1 -1
  2. reflex/.templates/jinja/web/pages/_app.js.jinja2 +7 -7
  3. reflex/.templates/jinja/web/pages/utils.js.jinja2 +2 -2
  4. reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js +1 -4
  5. reflex/.templates/web/utils/state.js +65 -36
  6. reflex/__init__.py +4 -17
  7. reflex/__init__.pyi +1 -2
  8. reflex/app.py +244 -115
  9. reflex/app_mixins/lifespan.py +9 -9
  10. reflex/app_mixins/middleware.py +6 -6
  11. reflex/app_module_for_backend.py +3 -7
  12. reflex/base.py +7 -7
  13. reflex/compiler/compiler.py +8 -0
  14. reflex/compiler/utils.py +35 -6
  15. reflex/components/base/bare.py +1 -1
  16. reflex/components/base/error_boundary.py +2 -1
  17. reflex/components/base/error_boundary.pyi +2 -1
  18. reflex/components/base/meta.py +2 -2
  19. reflex/components/base/strict_mode.py +10 -0
  20. reflex/components/base/strict_mode.pyi +57 -0
  21. reflex/components/component.py +38 -77
  22. reflex/components/core/banner.py +83 -4
  23. reflex/components/core/banner.pyi +86 -0
  24. reflex/components/core/breakpoints.py +3 -1
  25. reflex/components/core/client_side_routing.py +1 -1
  26. reflex/components/core/client_side_routing.pyi +1 -1
  27. reflex/components/core/cond.py +9 -10
  28. reflex/components/core/debounce.py +1 -1
  29. reflex/components/core/foreach.py +23 -3
  30. reflex/components/core/html.py +1 -1
  31. reflex/components/core/match.py +5 -5
  32. reflex/components/core/sticky.py +160 -0
  33. reflex/components/core/sticky.pyi +449 -0
  34. reflex/components/core/upload.py +2 -2
  35. reflex/components/datadisplay/code.py +5 -14
  36. reflex/components/datadisplay/dataeditor.py +7 -4
  37. reflex/components/datadisplay/logo.py +13 -8
  38. reflex/components/datadisplay/shiki_code_block.py +14 -9
  39. reflex/components/dynamic.py +22 -3
  40. reflex/components/el/constants/reflex.py +1 -1
  41. reflex/components/el/element.py +1 -1
  42. reflex/components/el/elements/forms.py +4 -4
  43. reflex/components/el/elements/forms.pyi +4 -4
  44. reflex/components/lucide/icon.py +46 -8
  45. reflex/components/lucide/icon.pyi +54 -0
  46. reflex/components/markdown/markdown.py +10 -8
  47. reflex/components/moment/moment.py +2 -2
  48. reflex/components/next/image.py +16 -4
  49. reflex/components/next/image.pyi +4 -2
  50. reflex/components/next/link.py +1 -1
  51. reflex/components/plotly/plotly.py +5 -5
  52. reflex/components/props.py +3 -3
  53. reflex/components/radix/__init__.pyi +1 -1
  54. reflex/components/radix/primitives/accordion.py +9 -5
  55. reflex/components/radix/primitives/accordion.pyi +3 -1
  56. reflex/components/radix/primitives/drawer.py +5 -2
  57. reflex/components/radix/primitives/drawer.pyi +4 -4
  58. reflex/components/radix/primitives/form.pyi +6 -6
  59. reflex/components/radix/primitives/progress.py +1 -1
  60. reflex/components/radix/primitives/slider.py +1 -1
  61. reflex/components/radix/themes/color_mode.py +11 -9
  62. reflex/components/radix/themes/components/alert_dialog.py +3 -0
  63. reflex/components/radix/themes/components/card.py +1 -1
  64. reflex/components/radix/themes/components/card.pyi +1 -1
  65. reflex/components/radix/themes/components/context_menu.py +5 -0
  66. reflex/components/radix/themes/components/dialog.py +3 -0
  67. reflex/components/radix/themes/components/dropdown_menu.py +5 -0
  68. reflex/components/radix/themes/components/hover_card.py +3 -0
  69. reflex/components/radix/themes/components/icon_button.py +2 -2
  70. reflex/components/radix/themes/components/icon_button.pyi +1 -0
  71. reflex/components/radix/themes/components/popover.py +3 -0
  72. reflex/components/radix/themes/components/radio_cards.py +2 -0
  73. reflex/components/radix/themes/components/radio_group.py +1 -1
  74. reflex/components/radix/themes/components/select.py +3 -0
  75. reflex/components/radix/themes/components/tabs.py +3 -0
  76. reflex/components/radix/themes/components/text_area.py +12 -0
  77. reflex/components/radix/themes/components/text_area.pyi +2 -0
  78. reflex/components/radix/themes/components/text_field.py +1 -1
  79. reflex/components/radix/themes/components/tooltip.py +3 -1
  80. reflex/components/radix/themes/components/tooltip.pyi +1 -0
  81. reflex/components/radix/themes/layout/__init__.pyi +1 -1
  82. reflex/components/radix/themes/layout/list.py +2 -2
  83. reflex/components/radix/themes/layout/stack.py +2 -2
  84. reflex/components/radix/themes/typography/link.py +1 -1
  85. reflex/components/radix/themes/typography/text.py +2 -2
  86. reflex/components/react_player/react_player.py +1 -1
  87. reflex/components/recharts/__init__.py +2 -0
  88. reflex/components/recharts/__init__.pyi +2 -0
  89. reflex/components/recharts/charts.py +15 -15
  90. reflex/components/recharts/general.py +19 -4
  91. reflex/components/recharts/general.pyi +55 -4
  92. reflex/components/recharts/polar.py +2 -2
  93. reflex/components/recharts/recharts.py +4 -4
  94. reflex/components/sonner/toast.py +15 -13
  95. reflex/components/sonner/toast.pyi +6 -6
  96. reflex/components/suneditor/editor.py +6 -4
  97. reflex/components/suneditor/editor.pyi +2 -2
  98. reflex/components/tags/iter_tag.py +3 -3
  99. reflex/components/tags/tag.py +25 -3
  100. reflex/config.py +48 -20
  101. reflex/constants/__init__.py +1 -0
  102. reflex/constants/base.py +4 -1
  103. reflex/constants/compiler.py +5 -2
  104. reflex/constants/config.py +8 -1
  105. reflex/constants/installer.py +9 -9
  106. reflex/constants/style.py +1 -1
  107. reflex/custom_components/custom_components.py +9 -7
  108. reflex/event.py +137 -163
  109. reflex/experimental/__init__.py +19 -11
  110. reflex/experimental/client_state.py +53 -28
  111. reflex/experimental/hooks.py +5 -5
  112. reflex/experimental/layout.py +8 -5
  113. reflex/experimental/layout.pyi +1 -1
  114. reflex/experimental/misc.py +3 -3
  115. reflex/istate/wrappers.py +1 -1
  116. reflex/middleware/hydrate_middleware.py +2 -2
  117. reflex/model.py +11 -6
  118. reflex/page.py +3 -3
  119. reflex/reflex.py +90 -19
  120. reflex/route.py +1 -1
  121. reflex/state.py +358 -401
  122. reflex/style.py +27 -3
  123. reflex/testing.py +34 -39
  124. reflex/utils/build.py +6 -2
  125. reflex/utils/codespaces.py +1 -4
  126. reflex/utils/compat.py +6 -5
  127. reflex/utils/console.py +52 -21
  128. reflex/utils/exceptions.py +76 -26
  129. reflex/utils/exec.py +69 -74
  130. reflex/utils/export.py +6 -1
  131. reflex/utils/format.py +7 -39
  132. reflex/utils/imports.py +2 -2
  133. reflex/utils/lazy_loader.py +7 -1
  134. reflex/utils/path_ops.py +28 -14
  135. reflex/utils/prerequisites.py +324 -65
  136. reflex/utils/processes.py +45 -32
  137. reflex/utils/pyi_generator.py +30 -25
  138. reflex/utils/registry.py +4 -4
  139. reflex/utils/serializers.py +1 -1
  140. reflex/utils/telemetry.py +5 -4
  141. reflex/utils/types.py +42 -18
  142. reflex/vars/base.py +650 -333
  143. reflex/vars/datetime.py +6 -7
  144. reflex/vars/dep_tracking.py +344 -0
  145. reflex/vars/function.py +11 -5
  146. reflex/vars/number.py +31 -43
  147. reflex/vars/object.py +63 -62
  148. reflex/vars/sequence.py +79 -67
  149. {reflex-0.6.8a1.dist-info → reflex-0.7.0a1.dist-info}/METADATA +7 -10
  150. {reflex-0.6.8a1.dist-info → reflex-0.7.0a1.dist-info}/RECORD +153 -150
  151. {reflex-0.6.8a1.dist-info → reflex-0.7.0a1.dist-info}/WHEEL +1 -1
  152. reflex/experimental/assets.py +0 -37
  153. reflex/proxy.py +0 -119
  154. {reflex-0.6.8a1.dist-info → reflex-0.7.0a1.dist-info}/LICENSE +0 -0
  155. {reflex-0.6.8a1.dist-info → reflex-0.7.0a1.dist-info}/entry_points.txt +0 -0
reflex/utils/exec.py CHANGED
@@ -71,7 +71,9 @@ def notify_backend():
71
71
  # run_process_and_launch_url is assumed to be used
72
72
  # only to launch the frontend
73
73
  # If this is not the case, might have to change the logic
74
- def run_process_and_launch_url(run_command: list[str], backend_present=True):
74
+ def run_process_and_launch_url(
75
+ run_command: list[str | None], backend_present: bool = True
76
+ ):
75
77
  """Run the process and launch the URL.
76
78
 
77
79
  Args:
@@ -89,7 +91,7 @@ def run_process_and_launch_url(run_command: list[str], backend_present=True):
89
91
  if process is None:
90
92
  kwargs = {}
91
93
  if constants.IS_WINDOWS and backend_present:
92
- kwargs["creationflags"] = subprocess.CREATE_NEW_PROCESS_GROUP # type: ignore
94
+ kwargs["creationflags"] = subprocess.CREATE_NEW_PROCESS_GROUP # pyright: ignore [reportAttributeAccessIssue]
93
95
  process = processes.new_process(
94
96
  run_command,
95
97
  cwd=get_web_dir(),
@@ -134,7 +136,7 @@ def run_process_and_launch_url(run_command: list[str], backend_present=True):
134
136
  break # while True
135
137
 
136
138
 
137
- def run_frontend(root: Path, port: str, backend_present=True):
139
+ def run_frontend(root: Path, port: str, backend_present: bool = True):
138
140
  """Run the frontend.
139
141
 
140
142
  Args:
@@ -151,12 +153,12 @@ def run_frontend(root: Path, port: str, backend_present=True):
151
153
  console.rule("[bold green]App Running")
152
154
  os.environ["PORT"] = str(get_config().frontend_port if port is None else port)
153
155
  run_process_and_launch_url(
154
- [prerequisites.get_package_manager(), "run", "dev"], # type: ignore
156
+ [prerequisites.get_package_manager(), "run", "dev"],
155
157
  backend_present,
156
158
  )
157
159
 
158
160
 
159
- def run_frontend_prod(root: Path, port: str, backend_present=True):
161
+ def run_frontend_prod(root: Path, port: str, backend_present: bool = True):
160
162
  """Run the frontend.
161
163
 
162
164
  Args:
@@ -173,7 +175,7 @@ def run_frontend_prod(root: Path, port: str, backend_present=True):
173
175
  # Run the frontend in production mode.
174
176
  console.rule("[bold green]App Running")
175
177
  run_process_and_launch_url(
176
- [prerequisites.get_package_manager(), "run", "prod"], # type: ignore
178
+ [prerequisites.get_package_manager(), "run", "prod"],
177
179
  backend_present,
178
180
  )
179
181
 
@@ -240,7 +242,32 @@ def run_backend(
240
242
  run_uvicorn_backend(host, port, loglevel)
241
243
 
242
244
 
243
- def run_uvicorn_backend(host, port, loglevel: LogLevel):
245
+ def get_reload_dirs() -> list[Path]:
246
+ """Get the reload directories for the backend.
247
+
248
+ Returns:
249
+ The reload directories for the backend.
250
+ """
251
+ config = get_config()
252
+ reload_dirs = [Path(config.app_name)]
253
+ if config.app_module is not None and config.app_module.__file__:
254
+ module_path = Path(config.app_module.__file__).resolve().parent
255
+
256
+ while module_path.parent.name:
257
+ if any(
258
+ sibling_file.name == "__init__.py"
259
+ for sibling_file in module_path.parent.iterdir()
260
+ ):
261
+ # go up a level to find dir without `__init__.py`
262
+ module_path = module_path.parent
263
+ else:
264
+ break
265
+
266
+ reload_dirs = [module_path]
267
+ return reload_dirs
268
+
269
+
270
+ def run_uvicorn_backend(host: str, port: int, loglevel: LogLevel):
244
271
  """Run the backend in development mode using Uvicorn.
245
272
 
246
273
  Args:
@@ -256,11 +283,11 @@ def run_uvicorn_backend(host, port, loglevel: LogLevel):
256
283
  port=port,
257
284
  log_level=loglevel.value,
258
285
  reload=True,
259
- reload_dirs=[get_config().app_name],
286
+ reload_dirs=list(map(str, get_reload_dirs())),
260
287
  )
261
288
 
262
289
 
263
- def run_granian_backend(host, port, loglevel: LogLevel):
290
+ def run_granian_backend(host: str, port: int, loglevel: LogLevel):
264
291
  """Run the backend in development mode using Granian.
265
292
 
266
293
  Args:
@@ -270,9 +297,11 @@ def run_granian_backend(host, port, loglevel: LogLevel):
270
297
  """
271
298
  console.debug("Using Granian for backend")
272
299
  try:
273
- from granian import Granian # type: ignore
274
- from granian.constants import Interfaces # type: ignore
275
- from granian.log import LogLevels # type: ignore
300
+ from granian import Granian # pyright: ignore [reportMissingImports]
301
+ from granian.constants import ( # pyright: ignore [reportMissingImports]
302
+ Interfaces,
303
+ )
304
+ from granian.log import LogLevels # pyright: ignore [reportMissingImports]
276
305
 
277
306
  Granian(
278
307
  target=get_granian_target(),
@@ -281,8 +310,8 @@ def run_granian_backend(host, port, loglevel: LogLevel):
281
310
  interface=Interfaces.ASGI,
282
311
  log_level=LogLevels(loglevel.value),
283
312
  reload=True,
284
- reload_paths=[Path(get_config().app_name)],
285
- reload_ignore_dirs=[".web"],
313
+ reload_paths=get_reload_dirs(),
314
+ reload_ignore_dirs=[".web", ".states"],
286
315
  ).serve()
287
316
  except ImportError:
288
317
  console.error(
@@ -325,7 +354,7 @@ def run_backend_prod(
325
354
  run_uvicorn_backend_prod(host, port, loglevel)
326
355
 
327
356
 
328
- def run_uvicorn_backend_prod(host, port, loglevel):
357
+ def run_uvicorn_backend_prod(host: str, port: int, loglevel: LogLevel):
329
358
  """Run the backend in production mode using Uvicorn.
330
359
 
331
360
  Args:
@@ -339,11 +368,11 @@ def run_uvicorn_backend_prod(host, port, loglevel):
339
368
 
340
369
  app_module = get_app_module()
341
370
 
342
- RUN_BACKEND_PROD = f"gunicorn --worker-class {config.gunicorn_worker_class} --max-requests {config.gunicorn_max_requests} --max-requests-jitter {config.gunicorn_max_requests_jitter} --preload --timeout {config.timeout} --log-level critical".split()
343
- RUN_BACKEND_PROD_WINDOWS = f"uvicorn --limit-max-requests {config.gunicorn_max_requests} --timeout-keep-alive {config.timeout}".split()
371
+ run_backend_prod = f"gunicorn --worker-class {config.gunicorn_worker_class} --max-requests {config.gunicorn_max_requests} --max-requests-jitter {config.gunicorn_max_requests_jitter} --preload --timeout {config.timeout} --log-level critical".split()
372
+ run_backend_prod_windows = f"uvicorn --limit-max-requests {config.gunicorn_max_requests} --timeout-keep-alive {config.timeout}".split()
344
373
  command = (
345
374
  [
346
- *RUN_BACKEND_PROD_WINDOWS,
375
+ *run_backend_prod_windows,
347
376
  "--host",
348
377
  host,
349
378
  "--port",
@@ -352,7 +381,7 @@ def run_uvicorn_backend_prod(host, port, loglevel):
352
381
  ]
353
382
  if constants.IS_WINDOWS
354
383
  else [
355
- *RUN_BACKEND_PROD,
384
+ *run_backend_prod,
356
385
  "--bind",
357
386
  f"{host}:{port}",
358
387
  "--threads",
@@ -377,7 +406,7 @@ def run_uvicorn_backend_prod(host, port, loglevel):
377
406
  )
378
407
 
379
408
 
380
- def run_granian_backend_prod(host, port, loglevel):
409
+ def run_granian_backend_prod(host: str, port: int, loglevel: LogLevel):
381
410
  """Run the backend in production mode using Granian.
382
411
 
383
412
  Args:
@@ -388,7 +417,9 @@ def run_granian_backend_prod(host, port, loglevel):
388
417
  from reflex.utils import processes
389
418
 
390
419
  try:
391
- from granian.constants import Interfaces # type: ignore
420
+ from granian.constants import ( # pyright: ignore [reportMissingImports]
421
+ Interfaces,
422
+ )
392
423
 
393
424
  command = [
394
425
  "granian",
@@ -442,22 +473,22 @@ def output_system_info():
442
473
 
443
474
  system = platform.system()
444
475
 
476
+ fnm_info = f"[FNM {prerequisites.get_fnm_version()} (Expected: {constants.Fnm.VERSION}) (PATH: {constants.Fnm.EXE})]"
477
+
445
478
  if system != "Windows" or (
446
479
  system == "Windows" and prerequisites.is_windows_bun_supported()
447
480
  ):
448
481
  dependencies.extend(
449
482
  [
450
- f"[FNM {prerequisites.get_fnm_version()} (Expected: {constants.Fnm.VERSION}) (PATH: {constants.Fnm.EXE})]",
451
- f"[Bun {prerequisites.get_bun_version()} (Expected: {constants.Bun.VERSION}) (PATH: {config.bun_path})]",
483
+ fnm_info,
484
+ f"[Bun {prerequisites.get_bun_version()} (Expected: {constants.Bun.VERSION}) (PATH: {path_ops.get_bun_path()})]",
452
485
  ],
453
486
  )
454
487
  else:
455
- dependencies.append(
456
- f"[FNM {prerequisites.get_fnm_version()} (Expected: {constants.Fnm.VERSION}) (PATH: {constants.Fnm.EXE})]",
457
- )
488
+ dependencies.append(fnm_info)
458
489
 
459
490
  if system == "Linux":
460
- import distro # type: ignore
491
+ import distro # pyright: ignore[reportMissingImports]
461
492
 
462
493
  os_version = distro.name(pretty=True)
463
494
  else:
@@ -469,11 +500,11 @@ def output_system_info():
469
500
  console.debug(f"{dep}")
470
501
 
471
502
  console.debug(
472
- f"Using package installer at: {prerequisites.get_install_package_manager(on_failure_return_none=True)}" # type: ignore
503
+ f"Using package installer at: {prerequisites.get_install_package_manager(on_failure_return_none=True)}"
473
504
  )
474
505
  console.debug(
475
506
  f"Using package executer at: {prerequisites.get_package_manager(on_failure_return_none=True)}"
476
- ) # type: ignore
507
+ )
477
508
  if system != "Windows":
478
509
  console.debug(f"Unzip path: {path_ops.which('unzip')}")
479
510
 
@@ -487,56 +518,20 @@ def is_testing_env() -> bool:
487
518
  return constants.PYTEST_CURRENT_TEST in os.environ
488
519
 
489
520
 
490
- def is_prod_mode() -> bool:
491
- """Check if the app is running in production mode.
521
+ def is_in_app_harness() -> bool:
522
+ """Whether the app is running in the app harness.
492
523
 
493
524
  Returns:
494
- True if the app is running in production mode or False if running in dev mode.
525
+ True if the app is running in the app harness.
495
526
  """
496
- current_mode = environment.REFLEX_ENV_MODE.get()
497
- return current_mode == constants.Env.PROD
527
+ return constants.APP_HARNESS_FLAG in os.environ
498
528
 
499
529
 
500
- def is_frontend_only() -> bool:
501
- """Check if the app is running in frontend-only mode.
502
-
503
- Returns:
504
- True if the app is running in frontend-only mode.
505
- """
506
- console.deprecate(
507
- "is_frontend_only() is deprecated and will be removed in a future release.",
508
- reason="Use `environment.REFLEX_FRONTEND_ONLY.get()` instead.",
509
- deprecation_version="0.6.5",
510
- removal_version="0.7.0",
511
- )
512
- return environment.REFLEX_FRONTEND_ONLY.get()
513
-
514
-
515
- def is_backend_only() -> bool:
516
- """Check if the app is running in backend-only mode.
517
-
518
- Returns:
519
- True if the app is running in backend-only mode.
520
- """
521
- console.deprecate(
522
- "is_backend_only() is deprecated and will be removed in a future release.",
523
- reason="Use `environment.REFLEX_BACKEND_ONLY.get()` instead.",
524
- deprecation_version="0.6.5",
525
- removal_version="0.7.0",
526
- )
527
- return environment.REFLEX_BACKEND_ONLY.get()
528
-
529
-
530
- def should_skip_compile() -> bool:
531
- """Whether the app should skip compile.
530
+ def is_prod_mode() -> bool:
531
+ """Check if the app is running in production mode.
532
532
 
533
533
  Returns:
534
- True if the app should skip compile.
534
+ True if the app is running in production mode or False if running in dev mode.
535
535
  """
536
- console.deprecate(
537
- "should_skip_compile() is deprecated and will be removed in a future release.",
538
- reason="Use `environment.REFLEX_SKIP_COMPILE.get()` instead.",
539
- deprecation_version="0.6.5",
540
- removal_version="0.7.0",
541
- )
542
- return environment.REFLEX_SKIP_COMPILE.get()
536
+ current_mode = environment.REFLEX_ENV_MODE.get()
537
+ return current_mode == constants.Env.PROD
reflex/utils/export.py CHANGED
@@ -4,7 +4,7 @@ from pathlib import Path
4
4
  from typing import Optional
5
5
 
6
6
  from reflex import constants
7
- from reflex.config import get_config
7
+ from reflex.config import environment, get_config
8
8
  from reflex.utils import build, console, exec, prerequisites, telemetry
9
9
 
10
10
  config = get_config()
@@ -18,6 +18,7 @@ def export(
18
18
  upload_db_file: bool = False,
19
19
  api_url: Optional[str] = None,
20
20
  deploy_url: Optional[str] = None,
21
+ env: constants.Env = constants.Env.PROD,
21
22
  loglevel: constants.LogLevel = console._LOG_LEVEL,
22
23
  ):
23
24
  """Export the app to a zip file.
@@ -30,11 +31,15 @@ def export(
30
31
  upload_db_file: Whether to upload the database file. Defaults to False.
31
32
  api_url: The API URL to use. Defaults to None.
32
33
  deploy_url: The deploy URL to use. Defaults to None.
34
+ env: The environment to use. Defaults to constants.Env.PROD.
33
35
  loglevel: The log level to use. Defaults to console._LOG_LEVEL.
34
36
  """
35
37
  # Set the log level.
36
38
  console.set_log_level(loglevel)
37
39
 
40
+ # Set env mode in the environment
41
+ environment.REFLEX_ENV_MODE.set(env)
42
+
38
43
  # Override the config url values if provided.
39
44
  if api_url is not None:
40
45
  config.api_url = str(api_url)
reflex/utils/format.py CHANGED
@@ -11,7 +11,6 @@ from typing import TYPE_CHECKING, Any, List, Optional, Union
11
11
  from reflex import constants
12
12
  from reflex.constants.state import FRONTEND_EVENT_STATE
13
13
  from reflex.utils import exceptions
14
- from reflex.utils.console import deprecate
15
14
 
16
15
  if TYPE_CHECKING:
17
16
  from reflex.components.component import ComponentStyle
@@ -221,7 +220,7 @@ def _escape_js_string(string: str) -> str:
221
220
  """
222
221
 
223
222
  # TODO: we may need to re-vist this logic after new Var API is implemented.
224
- def escape_outside_segments(segment):
223
+ def escape_outside_segments(segment: str):
225
224
  """Escape backticks in segments outside of `${}`.
226
225
 
227
226
  Args:
@@ -284,7 +283,7 @@ def format_var(var: Var) -> str:
284
283
  return str(var)
285
284
 
286
285
 
287
- def format_route(route: str, format_case=True) -> str:
286
+ def format_route(route: str, format_case: bool = True) -> str:
288
287
  """Format the given route.
289
288
 
290
289
  Args:
@@ -378,7 +377,7 @@ def format_prop(
378
377
 
379
378
  # For dictionaries, convert any properties to strings.
380
379
  elif isinstance(prop, dict):
381
- prop = serializers.serialize_dict(prop) # type: ignore
380
+ prop = serializers.serialize_dict(prop) # pyright: ignore [reportAttributeAccessIssue]
382
381
 
383
382
  else:
384
383
  # Dump the prop as JSON.
@@ -502,37 +501,6 @@ if TYPE_CHECKING:
502
501
  from reflex.vars import Var
503
502
 
504
503
 
505
- def format_event_chain(
506
- event_chain: EventChain | Var[EventChain],
507
- event_arg: Var | None = None,
508
- ) -> str:
509
- """DEPRECATED: format an event chain as a javascript invocation.
510
-
511
- Use str(rx.Var.create(event_chain)) instead.
512
-
513
- Args:
514
- event_chain: The event chain to format.
515
- event_arg: this argument is ignored.
516
-
517
- Returns:
518
- Compiled javascript code to queue the given event chain on the frontend.
519
- """
520
- deprecate(
521
- feature_name="format_event_chain",
522
- reason="Use str(rx.Var.create(event_chain)) instead",
523
- deprecation_version="0.6.0",
524
- removal_version="0.7.0",
525
- )
526
-
527
- from reflex.vars import Var
528
- from reflex.vars.function import ArgsFunctionOperation
529
-
530
- result = Var.create(event_chain)
531
- if isinstance(result, ArgsFunctionOperation):
532
- result = result._return_expr
533
- return str(result)
534
-
535
-
536
504
  def format_queue_events(
537
505
  events: EventType | None = None,
538
506
  args_spec: Optional[ArgsSpec] = None,
@@ -565,14 +533,14 @@ def format_queue_events(
565
533
  from reflex.vars import FunctionVar, Var
566
534
 
567
535
  if not events:
568
- return Var("(() => null)").to(FunctionVar, EventChain) # type: ignore
536
+ return Var("(() => null)").to(FunctionVar, EventChain)
569
537
 
570
538
  # If no spec is provided, the function will take no arguments.
571
539
  def _default_args_spec():
572
540
  return []
573
541
 
574
542
  # Construct the arguments that the function accepts.
575
- sig = inspect.signature(args_spec or _default_args_spec) # type: ignore
543
+ sig = inspect.signature(args_spec or _default_args_spec)
576
544
  if sig.parameters:
577
545
  arg_def = ",".join(f"_{p}" for p in sig.parameters)
578
546
  arg_def = f"({arg_def})"
@@ -589,7 +557,7 @@ def format_queue_events(
589
557
  if isinstance(spec, (EventHandler, EventSpec)):
590
558
  specs = [call_event_handler(spec, args_spec or _default_args_spec)]
591
559
  elif isinstance(spec, type(lambda: None)):
592
- specs = call_event_fn(spec, args_spec or _default_args_spec) # type: ignore
560
+ specs = call_event_fn(spec, args_spec or _default_args_spec) # pyright: ignore [reportAssignmentType, reportArgumentType]
593
561
  if isinstance(specs, Var):
594
562
  raise ValueError(
595
563
  f"Invalid event spec: {specs}. Expected a list of EventSpecs."
@@ -601,7 +569,7 @@ def format_queue_events(
601
569
  return Var(
602
570
  f"{arg_def} => {{queueEvents([{','.join(payloads)}], {constants.CompileVars.SOCKET}); "
603
571
  f"processEvent({constants.CompileVars.SOCKET})}}",
604
- ).to(FunctionVar, EventChain) # type: ignore
572
+ ).to(FunctionVar, EventChain)
605
573
 
606
574
 
607
575
  def format_query_params(router_data: dict[str, Any]) -> dict[str, str]:
reflex/utils/imports.py CHANGED
@@ -90,7 +90,7 @@ def collapse_imports(
90
90
  }
91
91
 
92
92
 
93
- @dataclasses.dataclass(order=True, frozen=True)
93
+ @dataclasses.dataclass(frozen=True)
94
94
  class ImportVar:
95
95
  """An import var."""
96
96
 
@@ -122,7 +122,7 @@ class ImportVar:
122
122
  """
123
123
  if self.alias:
124
124
  return (
125
- self.alias if self.is_default else " as ".join([self.tag, self.alias]) # type: ignore
125
+ self.alias if self.is_default else " as ".join([self.tag, self.alias]) # pyright: ignore [reportCallIssue,reportArgumentType]
126
126
  )
127
127
  else:
128
128
  return self.tag or ""
@@ -1,11 +1,17 @@
1
1
  """Module to implement lazy loading in reflex."""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  import copy
4
6
 
5
7
  import lazy_loader as lazy
6
8
 
7
9
 
8
- def attach(package_name, submodules=None, submod_attrs=None):
10
+ def attach(
11
+ package_name: str,
12
+ submodules: set | None = None,
13
+ submod_attrs: dict | None = None,
14
+ ):
9
15
  """Replaces a package's __getattr__, __dir__, and __all__ attributes using lazy.attach.
10
16
  The lazy loader __getattr__ doesn't support tuples as list values. We needed to add
11
17
  this functionality (tuples) in Reflex to support 'import as _' statements. This function
reflex/utils/path_ops.py CHANGED
@@ -9,7 +9,7 @@ import shutil
9
9
  from pathlib import Path
10
10
 
11
11
  from reflex import constants
12
- from reflex.config import environment
12
+ from reflex.config import environment, get_config
13
13
 
14
14
  # Shorthand for join.
15
15
  join = os.linesep.join
@@ -118,7 +118,7 @@ def ln(src: str | Path, dest: str | Path, overwrite: bool = False) -> bool:
118
118
  return True
119
119
 
120
120
 
121
- def which(program: str | Path) -> str | Path | None:
121
+ def which(program: str | Path) -> Path | None:
122
122
  """Find the path to an executable.
123
123
 
124
124
  Args:
@@ -127,7 +127,8 @@ def which(program: str | Path) -> str | Path | None:
127
127
  Returns:
128
128
  The path to the executable.
129
129
  """
130
- return shutil.which(str(program))
130
+ which_result = shutil.which(program)
131
+ return Path(which_result) if which_result else None
131
132
 
132
133
 
133
134
  def use_system_node() -> bool:
@@ -156,12 +157,12 @@ def get_node_bin_path() -> Path | None:
156
157
  """
157
158
  bin_path = Path(constants.Node.BIN_PATH)
158
159
  if not bin_path.exists():
159
- str_path = which("node")
160
- return Path(str_path).parent.resolve() if str_path else None
161
- return bin_path.resolve()
160
+ path = which("node")
161
+ return path.parent.absolute() if path else None
162
+ return bin_path.absolute()
162
163
 
163
164
 
164
- def get_node_path() -> str | None:
165
+ def get_node_path() -> Path | None:
165
166
  """Get the node binary path.
166
167
 
167
168
  Returns:
@@ -169,12 +170,11 @@ def get_node_path() -> str | None:
169
170
  """
170
171
  node_path = Path(constants.Node.PATH)
171
172
  if use_system_node() or not node_path.exists():
172
- system_node_path = which("node")
173
- return str(system_node_path) if system_node_path else None
174
- return str(node_path)
173
+ node_path = which("node")
174
+ return node_path
175
175
 
176
176
 
177
- def get_npm_path() -> str | None:
177
+ def get_npm_path() -> Path | None:
178
178
  """Get npm binary path.
179
179
 
180
180
  Returns:
@@ -182,9 +182,20 @@ def get_npm_path() -> str | None:
182
182
  """
183
183
  npm_path = Path(constants.Node.NPM_PATH)
184
184
  if use_system_node() or not npm_path.exists():
185
- system_npm_path = which("npm")
186
- return str(system_npm_path) if system_npm_path else None
187
- return str(npm_path)
185
+ npm_path = which("npm")
186
+ return npm_path.absolute() if npm_path else None
187
+
188
+
189
+ def get_bun_path() -> Path | None:
190
+ """Get bun binary path.
191
+
192
+ Returns:
193
+ The path to the bun binary file.
194
+ """
195
+ bun_path = get_config().bun_path
196
+ if use_system_bun() or not bun_path.exists():
197
+ bun_path = which("bun")
198
+ return bun_path.absolute() if bun_path else None
188
199
 
189
200
 
190
201
  def update_json_file(file_path: str | Path, update_dict: dict[str, int | str]):
@@ -196,6 +207,9 @@ def update_json_file(file_path: str | Path, update_dict: dict[str, int | str]):
196
207
  """
197
208
  fp = Path(file_path)
198
209
 
210
+ # Create the parent directory if it doesn't exist.
211
+ fp.parent.mkdir(parents=True, exist_ok=True)
212
+
199
213
  # Create the file if it doesn't exist.
200
214
  fp.touch(exist_ok=True)
201
215