reflex 0.7.8__py3-none-any.whl → 0.7.9a1__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 (37) hide show
  1. reflex/.templates/jinja/web/tailwind.config.js.jinja2 +65 -31
  2. reflex/.templates/web/utils/state.js +11 -1
  3. reflex/app.py +185 -79
  4. reflex/app_mixins/lifespan.py +2 -2
  5. reflex/compiler/compiler.py +31 -4
  6. reflex/components/component.py +39 -57
  7. reflex/components/core/upload.py +8 -0
  8. reflex/components/dynamic.py +9 -1
  9. reflex/components/markdown/markdown.py +0 -21
  10. reflex/components/radix/primitives/accordion.py +1 -1
  11. reflex/components/radix/primitives/form.py +1 -1
  12. reflex/components/radix/primitives/progress.py +1 -1
  13. reflex/components/radix/primitives/slider.py +1 -1
  14. reflex/components/radix/themes/color_mode.py +1 -1
  15. reflex/components/radix/themes/color_mode.pyi +1 -1
  16. reflex/components/recharts/recharts.py +2 -2
  17. reflex/components/sonner/toast.py +1 -1
  18. reflex/config.py +4 -7
  19. reflex/constants/base.py +21 -0
  20. reflex/constants/installer.py +6 -6
  21. reflex/custom_components/custom_components.py +67 -64
  22. reflex/event.py +2 -0
  23. reflex/reflex.py +276 -265
  24. reflex/testing.py +30 -24
  25. reflex/utils/codespaces.py +6 -2
  26. reflex/utils/console.py +4 -3
  27. reflex/utils/exec.py +60 -24
  28. reflex/utils/format.py +17 -2
  29. reflex/utils/prerequisites.py +43 -30
  30. reflex/utils/processes.py +6 -6
  31. reflex/utils/types.py +11 -6
  32. reflex/vars/base.py +19 -1
  33. {reflex-0.7.8.dist-info → reflex-0.7.9a1.dist-info}/METADATA +6 -9
  34. {reflex-0.7.8.dist-info → reflex-0.7.9a1.dist-info}/RECORD +37 -37
  35. {reflex-0.7.8.dist-info → reflex-0.7.9a1.dist-info}/WHEEL +0 -0
  36. {reflex-0.7.8.dist-info → reflex-0.7.9a1.dist-info}/entry_points.txt +0 -0
  37. {reflex-0.7.8.dist-info → reflex-0.7.9a1.dist-info}/licenses/LICENSE +0 -0
reflex/testing.py CHANGED
@@ -34,7 +34,7 @@ import reflex.utils.format
34
34
  import reflex.utils.prerequisites
35
35
  import reflex.utils.processes
36
36
  from reflex.components.component import CustomComponent
37
- from reflex.config import environment
37
+ from reflex.config import environment, get_config
38
38
  from reflex.state import (
39
39
  BaseState,
40
40
  StateManager,
@@ -44,6 +44,7 @@ from reflex.state import (
44
44
  reload_state_module,
45
45
  )
46
46
  from reflex.utils import console
47
+ from reflex.utils.export import export
47
48
 
48
49
  try:
49
50
  from selenium import webdriver
@@ -116,7 +117,6 @@ class AppHarness:
116
117
  backend: uvicorn.Server | None = None
117
118
  state_manager: StateManager | None = None
118
119
  _frontends: list[WebDriver] = dataclasses.field(default_factory=list)
119
- _decorated_pages: list = dataclasses.field(default_factory=list)
120
120
 
121
121
  @classmethod
122
122
  def create(
@@ -253,11 +253,11 @@ class AppHarness:
253
253
  self._get_source_from_app_source(self.app_source),
254
254
  ]
255
255
  )
256
+ get_config().loglevel = reflex.constants.LogLevel.INFO
256
257
  with chdir(self.app_path):
257
258
  reflex.reflex._init(
258
259
  name=self.app_name,
259
260
  template=reflex.constants.Templates.DEFAULT,
260
- loglevel=reflex.constants.LogLevel.INFO,
261
261
  )
262
262
  self.app_module_path.write_text(source_code)
263
263
  else:
@@ -267,8 +267,6 @@ class AppHarness:
267
267
  with chdir(self.app_path):
268
268
  # ensure config and app are reloaded when testing different app
269
269
  reflex.config.get_config(reload=True)
270
- # Save decorated pages before importing the test app module
271
- before_decorated_pages = reflex.app.DECORATED_PAGES[self.app_name].copy()
272
270
  # Ensure the AppHarness test does not skip State assignment due to running via pytest
273
271
  os.environ.pop(reflex.constants.PYTEST_CURRENT_TEST, None)
274
272
  os.environ[reflex.constants.APP_HARNESS_FLAG] = "true"
@@ -276,12 +274,6 @@ class AppHarness:
276
274
  # Do not reload the module for pre-existing apps (only apps generated from source)
277
275
  reload=self.app_source is not None
278
276
  )
279
- # Save the pages that were added during testing
280
- self._decorated_pages = [
281
- p
282
- for p in reflex.app.DECORATED_PAGES[self.app_name]
283
- if p not in before_decorated_pages
284
- ]
285
277
  self.app_instance = self.app_module.app
286
278
  if self.app_instance and isinstance(
287
279
  self.app_instance._state_manager, StateManagerRedis
@@ -305,25 +297,37 @@ class AppHarness:
305
297
 
306
298
  original_shutdown = self.backend.shutdown
307
299
 
308
- async def _shutdown_redis(*args, **kwargs) -> None:
300
+ async def _shutdown(*args, **kwargs) -> None:
309
301
  # ensure redis is closed before event loop
310
- try:
311
- if self.app_instance is not None and isinstance(
312
- self.app_instance.state_manager, StateManagerRedis
313
- ):
302
+ if self.app_instance is not None and isinstance(
303
+ self.app_instance.state_manager, StateManagerRedis
304
+ ):
305
+ with contextlib.suppress(ValueError):
314
306
  await self.app_instance.state_manager.close()
307
+
308
+ # socketio shutdown handler
309
+ if self.app_instance is not None and self.app_instance.sio is not None:
310
+ with contextlib.suppress(TypeError):
311
+ await self.app_instance.sio.shutdown()
312
+
313
+ # sqlalchemy async engine shutdown handler
314
+ try:
315
+ async_engine = reflex.model.get_async_engine(None)
315
316
  except ValueError:
316
317
  pass
318
+ else:
319
+ await async_engine.dispose()
320
+
317
321
  await original_shutdown(*args, **kwargs)
318
322
 
319
- return _shutdown_redis
323
+ return _shutdown
320
324
 
321
325
  def _start_backend(self, port: int = 0):
322
- if self.app_instance is None or self.app_instance.api is None:
326
+ if self.app_instance is None or self.app_instance._api is None:
323
327
  raise RuntimeError("App was not initialized.")
324
328
  self.backend = uvicorn.Server(
325
329
  uvicorn.Config(
326
- app=self.app_instance.api,
330
+ app=self.app_instance._api,
327
331
  host="127.0.0.1",
328
332
  port=port,
329
333
  )
@@ -488,10 +492,6 @@ class AppHarness:
488
492
  if self.frontend_output_thread is not None:
489
493
  self.frontend_output_thread.join()
490
494
 
491
- # Cleanup decorated pages added during testing
492
- for page in self._decorated_pages:
493
- reflex.app.DECORATED_PAGES[self.app_name].remove(page)
494
-
495
495
  def __exit__(self, *excinfo) -> None:
496
496
  """Contextmanager protocol for `stop()`.
497
497
 
@@ -934,7 +934,13 @@ class AppHarnessProd(AppHarness):
934
934
  config.api_url = "http://{}:{}".format(
935
935
  *self._poll_for_servers().getsockname(),
936
936
  )
937
- reflex.reflex.export(
937
+
938
+ get_config().loglevel = reflex.constants.LogLevel.INFO
939
+
940
+ if reflex.utils.prerequisites.needs_reinit(frontend=True):
941
+ reflex.reflex._init(name=get_config().app_name)
942
+
943
+ export(
938
944
  zipping=False,
939
945
  frontend=True,
940
946
  backend=False,
@@ -4,7 +4,8 @@ from __future__ import annotations
4
4
 
5
5
  import os
6
6
 
7
- from fastapi.responses import HTMLResponse
7
+ from starlette.requests import Request
8
+ from starlette.responses import HTMLResponse
8
9
 
9
10
  from reflex.components.base.script import Script
10
11
  from reflex.components.component import Component
@@ -74,9 +75,12 @@ def codespaces_auto_redirect() -> list[Component]:
74
75
  return []
75
76
 
76
77
 
77
- async def auth_codespace() -> HTMLResponse:
78
+ async def auth_codespace(_request: Request) -> HTMLResponse:
78
79
  """Page automatically redirecting back to the app after authenticating a codespace port forward.
79
80
 
81
+ Args:
82
+ _request: The request object.
83
+
80
84
  Returns:
81
85
  An HTML response with an embedded script to redirect back to the app.
82
86
  """
reflex/utils/console.py CHANGED
@@ -47,7 +47,7 @@ _EMITTED_LOGS = set()
47
47
  _EMITTED_PRINTS = set()
48
48
 
49
49
 
50
- def set_log_level(log_level: LogLevel):
50
+ def set_log_level(log_level: LogLevel | None):
51
51
  """Set the log level.
52
52
 
53
53
  Args:
@@ -56,6 +56,8 @@ def set_log_level(log_level: LogLevel):
56
56
  Raises:
57
57
  TypeError: If the log level is a string.
58
58
  """
59
+ if log_level is None:
60
+ return
59
61
  if not isinstance(log_level, LogLevel):
60
62
  raise TypeError(
61
63
  f"log_level must be a LogLevel enum value, got {log_level} of type {type(log_level)} instead."
@@ -193,13 +195,12 @@ def warn(msg: str, dedupe: bool = False, **kwargs):
193
195
 
194
196
  def _get_first_non_framework_frame() -> FrameType | None:
195
197
  import click
196
- import typer
197
198
  import typing_extensions
198
199
 
199
200
  import reflex as rx
200
201
 
201
202
  # Exclude utility modules that should never be the source of deprecated reflex usage.
202
- exclude_modules = [click, rx, typer, typing_extensions]
203
+ exclude_modules = [click, rx, typing_extensions]
203
204
  exclude_roots = [
204
205
  p.parent.resolve() if (p := Path(file)).name == "__init__.py" else p.resolve()
205
206
  for m in exclude_modules
reflex/utils/exec.py CHANGED
@@ -189,11 +189,9 @@ def run_frontend_prod(root: Path, port: str, backend_present: bool = True):
189
189
 
190
190
  @once
191
191
  def _warn_user_about_uvicorn():
192
- # When we eventually switch to Granian by default, we should enable this warning.
193
- if False:
194
- console.warn(
195
- "Using Uvicorn for backend as it is installed. This behavior will change in 0.8.0 to use Granian by default."
196
- )
192
+ console.warn(
193
+ "Using Uvicorn for backend as it is installed. This behavior will change in 0.8.0 to use Granian by default."
194
+ )
197
195
 
198
196
 
199
197
  def should_use_granian():
@@ -202,8 +200,8 @@ def should_use_granian():
202
200
  Returns:
203
201
  True if Granian should be used.
204
202
  """
205
- if environment.REFLEX_USE_GRANIAN.get():
206
- return True
203
+ if environment.REFLEX_USE_GRANIAN.is_set():
204
+ return environment.REFLEX_USE_GRANIAN.get()
207
205
  if (
208
206
  importlib.util.find_spec("uvicorn") is None
209
207
  or importlib.util.find_spec("gunicorn") is None
@@ -219,9 +217,51 @@ def get_app_module():
219
217
  Returns:
220
218
  The app module for the backend.
221
219
  """
222
- config = get_config()
220
+ return get_config().module
221
+
222
+
223
+ def get_app_instance():
224
+ """Get the app module for the backend.
225
+
226
+ Returns:
227
+ The app module for the backend.
228
+ """
229
+ return f"{get_app_module()}:{constants.CompileVars.APP}"
230
+
231
+
232
+ def get_app_file() -> Path:
233
+ """Get the app file for the backend.
234
+
235
+ Returns:
236
+ The app file for the backend.
237
+
238
+ Raises:
239
+ ImportError: If the app module is not found.
240
+ """
241
+ current_working_dir = str(Path.cwd())
242
+ if current_working_dir not in sys.path:
243
+ # Add the current working directory to sys.path
244
+ sys.path.insert(0, current_working_dir)
245
+ module_spec = importlib.util.find_spec(get_app_module())
246
+ if module_spec is None:
247
+ raise ImportError(
248
+ f"Module {get_app_module()} not found. Make sure the module is installed."
249
+ )
250
+ file_name = module_spec.origin
251
+ if file_name is None:
252
+ raise ImportError(
253
+ f"Module {get_app_module()} not found. Make sure the module is installed."
254
+ )
255
+ return Path(file_name).resolve()
256
+
257
+
258
+ def get_app_instance_from_file() -> str:
259
+ """Get the app module for the backend.
223
260
 
224
- return f"{config.module}:{constants.CompileVars.APP}"
261
+ Returns:
262
+ The app module for the backend.
263
+ """
264
+ return f"{get_app_file()}:{constants.CompileVars.APP}"
225
265
 
226
266
 
227
267
  def run_backend(
@@ -323,7 +363,7 @@ def run_uvicorn_backend(host: str, port: int, loglevel: LogLevel):
323
363
  import uvicorn
324
364
 
325
365
  uvicorn.run(
326
- app=f"{get_app_module()}",
366
+ app=f"{get_app_instance()}",
327
367
  factory=True,
328
368
  host=host,
329
369
  port=port,
@@ -349,7 +389,7 @@ def run_granian_backend(host: str, port: int, loglevel: LogLevel):
349
389
  from granian.server import MPServer as Granian
350
390
 
351
391
  Granian(
352
- target=get_app_module(),
392
+ target=get_app_instance_from_file(),
353
393
  factory=True,
354
394
  address=host,
355
395
  port=port,
@@ -367,14 +407,12 @@ def _deprecate_asgi_config(
367
407
  config_name: str,
368
408
  reason: str = "",
369
409
  ):
370
- # When we eventually switch to Granian by default, we should enable this deprecation.
371
- if False:
372
- console.deprecate(
373
- f"config.{config_name}",
374
- reason=reason,
375
- deprecation_version="0.7.5",
376
- removal_version="0.8.0",
377
- )
410
+ console.deprecate(
411
+ f"config.{config_name}",
412
+ reason=reason,
413
+ deprecation_version="0.7.9",
414
+ removal_version="0.8.0",
415
+ )
378
416
 
379
417
 
380
418
  @once
@@ -468,7 +506,7 @@ def run_uvicorn_backend_prod(host: str, port: int, loglevel: LogLevel):
468
506
 
469
507
  config = get_config()
470
508
 
471
- app_module = get_app_module()
509
+ app_module = get_app_instance()
472
510
 
473
511
  command = (
474
512
  [
@@ -568,7 +606,7 @@ def run_granian_backend_prod(host: str, port: int, loglevel: LogLevel):
568
606
  *("--host", host),
569
607
  *("--port", str(port)),
570
608
  *("--interface", str(Interfaces.ASGI)),
571
- *("--factory", get_app_module()),
609
+ *("--factory", get_app_instance_from_file()),
572
610
  ]
573
611
  processes.new_process(
574
612
  command,
@@ -613,9 +651,7 @@ def output_system_info():
613
651
  )
614
652
 
615
653
  if system == "Linux":
616
- import distro # pyright: ignore[reportMissingImports]
617
-
618
- os_version = distro.name(pretty=True)
654
+ os_version = platform.freedesktop_os_release().get("PRETTY_NAME", "Unknown")
619
655
  else:
620
656
  os_version = platform.version()
621
657
 
reflex/utils/format.py CHANGED
@@ -643,17 +643,32 @@ def format_ref(ref: str) -> str:
643
643
  return f"ref_{clean_ref}"
644
644
 
645
645
 
646
- def format_library_name(library_fullname: str):
646
+ def format_library_name(library_fullname: str | dict[str, Any]) -> str:
647
647
  """Format the name of a library.
648
648
 
649
649
  Args:
650
- library_fullname: The fullname of the library.
650
+ library_fullname: The library reference, either as a string or a dictionary with a 'name' key.
651
651
 
652
652
  Returns:
653
653
  The name without the @version if it was part of the name
654
+
655
+ Raises:
656
+ KeyError: If library_fullname is a dictionary without a 'name' key.
657
+ TypeError: If library_fullname or its 'name' value is not a string.
654
658
  """
659
+ # If input is a dictionary, extract the 'name' key
660
+ if isinstance(library_fullname, dict):
661
+ if "name" not in library_fullname:
662
+ raise KeyError("Dictionary input must contain a 'name' key")
663
+ library_fullname = library_fullname["name"]
664
+
665
+ # Process the library name as a string
666
+ if not isinstance(library_fullname, str):
667
+ raise TypeError("Library name must be a string")
668
+
655
669
  if library_fullname.startswith("https://"):
656
670
  return library_fullname
671
+
657
672
  lib, at, version = library_fullname.rpartition("@")
658
673
  if not lib:
659
674
  lib = at + version
@@ -27,8 +27,8 @@ from types import ModuleType
27
27
  from typing import NamedTuple
28
28
  from urllib.parse import urlparse
29
29
 
30
+ import click
30
31
  import httpx
31
- import typer
32
32
  from alembic.util.exc import CommandError
33
33
  from packaging import version
34
34
  from redis import Redis as RedisSync
@@ -371,7 +371,6 @@ def get_app(reload: bool = False) -> ModuleType:
371
371
  from reflex.utils import telemetry
372
372
 
373
373
  try:
374
- environment.RELOAD_CONFIG.set(reload)
375
374
  config = get_config()
376
375
 
377
376
  _check_app_name(config)
@@ -384,11 +383,14 @@ def get_app(reload: bool = False) -> ModuleType:
384
383
  else config.app_module
385
384
  )
386
385
  if reload:
386
+ from reflex.page import DECORATED_PAGES
387
387
  from reflex.state import reload_state_module
388
388
 
389
389
  # Reset rx.State subclasses to avoid conflict when reloading.
390
390
  reload_state_module(module=module)
391
391
 
392
+ DECORATED_PAGES.clear()
393
+
392
394
  # Reload the app module.
393
395
  importlib.reload(app)
394
396
  except Exception as ex:
@@ -515,7 +517,7 @@ def compile_or_validate_app(compile: bool = False) -> bool:
515
517
  else:
516
518
  validate_app()
517
519
  except Exception as e:
518
- if isinstance(e, typer.Exit):
520
+ if isinstance(e, click.exceptions.Exit):
519
521
  return False
520
522
 
521
523
  import traceback
@@ -619,14 +621,14 @@ def validate_app_name(app_name: str | None = None) -> str:
619
621
  console.error(
620
622
  f"The app directory cannot be named [bold]{constants.Reflex.MODULE_NAME}[/bold]."
621
623
  )
622
- raise typer.Exit(1)
624
+ raise click.exceptions.Exit(1)
623
625
 
624
626
  # Make sure the app name is standard for a python package name.
625
627
  if not re.match(r"^[a-zA-Z][a-zA-Z0-9_]*$", app_name):
626
628
  console.error(
627
629
  "The app directory name must start with a letter and can contain letters, numbers, and underscores."
628
630
  )
629
- raise typer.Exit(1)
631
+ raise click.exceptions.Exit(1)
630
632
 
631
633
  return app_name
632
634
 
@@ -685,7 +687,7 @@ def rename_app(new_app_name: str, loglevel: constants.LogLevel):
685
687
  console.error(
686
688
  "No rxconfig.py found. Make sure you are in the root directory of your app."
687
689
  )
688
- raise typer.Exit(1)
690
+ raise click.exceptions.Exit(1)
689
691
 
690
692
  sys.path.insert(0, str(Path.cwd()))
691
693
 
@@ -693,11 +695,11 @@ def rename_app(new_app_name: str, loglevel: constants.LogLevel):
693
695
  module_path = importlib.util.find_spec(config.module)
694
696
  if module_path is None:
695
697
  console.error(f"Could not find module {config.module}.")
696
- raise typer.Exit(1)
698
+ raise click.exceptions.Exit(1)
697
699
 
698
700
  if not module_path.origin:
699
701
  console.error(f"Could not find origin for module {config.module}.")
700
- raise typer.Exit(1)
702
+ raise click.exceptions.Exit(1)
701
703
  console.info(f"Renaming app directory to {new_app_name}.")
702
704
  process_directory(
703
705
  Path.cwd(),
@@ -860,7 +862,7 @@ def initialize_requirements_txt() -> bool:
860
862
  continue
861
863
  except Exception as e:
862
864
  console.error(f"Failed to read {requirements_file_path}.")
863
- raise typer.Exit(1) from e
865
+ raise click.exceptions.Exit(1) from e
864
866
  else:
865
867
  return False
866
868
 
@@ -905,7 +907,7 @@ def initialize_app_directory(
905
907
  console.error(
906
908
  f"Only {template_name=} should be provided, got {template_code_dir_name=}, {template_dir=}."
907
909
  )
908
- raise typer.Exit(1)
910
+ raise click.exceptions.Exit(1)
909
911
  template_code_dir_name = constants.Templates.Dirs.CODE
910
912
  template_dir = Path(constants.Templates.Dirs.BASE, "apps", template_name)
911
913
  else:
@@ -913,7 +915,7 @@ def initialize_app_directory(
913
915
  console.error(
914
916
  f"For `{template_name}` template, `template_code_dir_name` and `template_dir` should both be provided."
915
917
  )
916
- raise typer.Exit(1)
918
+ raise click.exceptions.Exit(1)
917
919
 
918
920
  console.debug(f"Using {template_name=} {template_dir=} {template_code_dir_name=}.")
919
921
 
@@ -1132,12 +1134,20 @@ def download_and_run(url: str, *args, show_status: bool = False, **env):
1132
1134
  args: The arguments to pass to the script.
1133
1135
  show_status: Whether to show the status of the script.
1134
1136
  env: The environment variables to use.
1137
+
1138
+ Raises:
1139
+ Exit: If the script fails to download.
1135
1140
  """
1136
1141
  # Download the script
1137
1142
  console.debug(f"Downloading {url}")
1138
- response = net.get(url)
1139
- if response.status_code != httpx.codes.OK:
1143
+ try:
1144
+ response = net.get(url)
1140
1145
  response.raise_for_status()
1146
+ except httpx.HTTPError as e:
1147
+ console.error(
1148
+ f"Failed to download bun install script. You can install or update bun manually from https://bun.sh \n{e}"
1149
+ )
1150
+ raise click.exceptions.Exit(1) from None
1141
1151
 
1142
1152
  # Save the script to a temporary file.
1143
1153
  script = Path(tempfile.NamedTemporaryFile().name)
@@ -1314,7 +1324,10 @@ def install_frontend_packages(packages: set[str], config: Config):
1314
1324
  "--legacy-peer-deps",
1315
1325
  "-d",
1316
1326
  constants.Tailwind.VERSION,
1317
- *((config.tailwind or {}).get("plugins", [])),
1327
+ *[
1328
+ plugin if isinstance(plugin, str) else plugin.get("name")
1329
+ for plugin in (config.tailwind or {}).get("plugins", [])
1330
+ ],
1318
1331
  ],
1319
1332
  fallbacks=fallbacks,
1320
1333
  analytics_enabled=True,
@@ -1368,7 +1381,7 @@ def needs_reinit(frontend: bool = True) -> bool:
1368
1381
  console.error(
1369
1382
  f"[cyan]{constants.Config.FILE}[/cyan] not found. Move to the root folder of your project, or run [bold]{constants.Reflex.MODULE_NAME} init[/bold] to start a new project."
1370
1383
  )
1371
- raise typer.Exit(1)
1384
+ raise click.exceptions.Exit(1)
1372
1385
 
1373
1386
  # Don't need to reinit if not running in frontend mode.
1374
1387
  if not frontend:
@@ -1433,7 +1446,7 @@ def validate_bun(bun_path: Path | None = None):
1433
1446
  console.error(
1434
1447
  "Failed to obtain bun version. Make sure the specified bun path in your config is correct."
1435
1448
  )
1436
- raise typer.Exit(1)
1449
+ raise click.exceptions.Exit(1)
1437
1450
  elif bun_version < version.parse(constants.Bun.MIN_VERSION):
1438
1451
  console.warn(
1439
1452
  f"Reflex requires bun version {constants.Bun.MIN_VERSION} or higher to run, but the detected version is "
@@ -1455,14 +1468,14 @@ def validate_frontend_dependencies(init: bool = True):
1455
1468
  try:
1456
1469
  get_js_package_executor(raise_on_none=True)
1457
1470
  except FileNotFoundError as e:
1458
- raise typer.Exit(1) from e
1471
+ raise click.exceptions.Exit(1) from e
1459
1472
 
1460
1473
  if prefer_npm_over_bun() and not check_node_version():
1461
1474
  node_version = get_node_version()
1462
1475
  console.error(
1463
1476
  f"Reflex requires node version {constants.Node.MIN_VERSION} or higher to run, but the detected version is {node_version}",
1464
1477
  )
1465
- raise typer.Exit(1)
1478
+ raise click.exceptions.Exit(1)
1466
1479
 
1467
1480
 
1468
1481
  def ensure_reflex_installation_id() -> int | None:
@@ -1609,17 +1622,17 @@ def prompt_for_template_options(templates: list[Template]) -> str:
1609
1622
 
1610
1623
  if not template:
1611
1624
  console.error("No template selected.")
1612
- raise typer.Exit(1)
1625
+ raise click.exceptions.Exit(1)
1613
1626
 
1614
1627
  try:
1615
1628
  template_index = int(template)
1616
1629
  except ValueError:
1617
1630
  console.error("Invalid template selected.")
1618
- raise typer.Exit(1) from None
1631
+ raise click.exceptions.Exit(1) from None
1619
1632
 
1620
1633
  if template_index < 0 or template_index >= len(templates):
1621
1634
  console.error("Invalid template selected.")
1622
- raise typer.Exit(1)
1635
+ raise click.exceptions.Exit(1)
1623
1636
 
1624
1637
  # Return the template.
1625
1638
  return templates[template_index].name
@@ -1699,7 +1712,7 @@ def create_config_init_app_from_remote_template(app_name: str, template_url: str
1699
1712
  temp_dir = tempfile.mkdtemp()
1700
1713
  except OSError as ose:
1701
1714
  console.error(f"Failed to create temp directory for download: {ose}")
1702
- raise typer.Exit(1) from ose
1715
+ raise click.exceptions.Exit(1) from ose
1703
1716
 
1704
1717
  # Use httpx GET with redirects to download the zip file.
1705
1718
  zip_file_path: Path = Path(temp_dir) / "template.zip"
@@ -1710,20 +1723,20 @@ def create_config_init_app_from_remote_template(app_name: str, template_url: str
1710
1723
  response.raise_for_status()
1711
1724
  except httpx.HTTPError as he:
1712
1725
  console.error(f"Failed to download the template: {he}")
1713
- raise typer.Exit(1) from he
1726
+ raise click.exceptions.Exit(1) from he
1714
1727
  try:
1715
1728
  zip_file_path.write_bytes(response.content)
1716
1729
  console.debug(f"Downloaded the zip to {zip_file_path}")
1717
1730
  except OSError as ose:
1718
1731
  console.error(f"Unable to write the downloaded zip to disk {ose}")
1719
- raise typer.Exit(1) from ose
1732
+ raise click.exceptions.Exit(1) from ose
1720
1733
 
1721
1734
  # Create a temp directory for the zip extraction.
1722
1735
  try:
1723
1736
  unzip_dir = Path(tempfile.mkdtemp())
1724
1737
  except OSError as ose:
1725
1738
  console.error(f"Failed to create temp directory for extracting zip: {ose}")
1726
- raise typer.Exit(1) from ose
1739
+ raise click.exceptions.Exit(1) from ose
1727
1740
 
1728
1741
  try:
1729
1742
  zipfile.ZipFile(zip_file_path).extractall(path=unzip_dir)
@@ -1731,11 +1744,11 @@ def create_config_init_app_from_remote_template(app_name: str, template_url: str
1731
1744
  # repo-name-branch/**/*, so we need to remove the top level directory.
1732
1745
  except Exception as uze:
1733
1746
  console.error(f"Failed to unzip the template: {uze}")
1734
- raise typer.Exit(1) from uze
1747
+ raise click.exceptions.Exit(1) from uze
1735
1748
 
1736
1749
  if len(subdirs := list(unzip_dir.iterdir())) != 1:
1737
1750
  console.error(f"Expected one directory in the zip, found {subdirs}")
1738
- raise typer.Exit(1)
1751
+ raise click.exceptions.Exit(1)
1739
1752
 
1740
1753
  template_dir = unzip_dir / subdirs[0]
1741
1754
  console.debug(f"Template folder is located at {template_dir}")
@@ -1797,7 +1810,7 @@ def validate_and_create_app_using_remote_template(
1797
1810
  console.print(
1798
1811
  f"Please use `reflex login` to access the '{template}' template."
1799
1812
  )
1800
- raise typer.Exit(3)
1813
+ raise click.exceptions.Exit(3)
1801
1814
 
1802
1815
  template_url = templates[template].code_url
1803
1816
  else:
@@ -1808,7 +1821,7 @@ def validate_and_create_app_using_remote_template(
1808
1821
  template_url = f"https://github.com/{path}/archive/main.zip"
1809
1822
  else:
1810
1823
  console.error(f"Template `{template}` not found or invalid.")
1811
- raise typer.Exit(1)
1824
+ raise click.exceptions.Exit(1)
1812
1825
 
1813
1826
  if template_url is None:
1814
1827
  return
@@ -1875,7 +1888,7 @@ def initialize_app(app_name: str, template: str | None = None) -> str | None:
1875
1888
  console.print(
1876
1889
  f"Go to the templates page ({constants.Templates.REFLEX_TEMPLATES_URL}) and copy the command to init with a template."
1877
1890
  )
1878
- raise typer.Exit(0)
1891
+ raise click.exceptions.Exit(0)
1879
1892
 
1880
1893
  # If the blank template is selected, create a blank app.
1881
1894
  if template in (constants.Templates.DEFAULT,):