Explorar el Código

Simplify `run_process_with_fallback` logic (#3089)

* Simplify `run_process_with_fallback` logic

Include log streaming for initial command and fallback command. This is
necessary because if the command produces significant output that is not
consumed, it can fill the OS pipe and block the process from running.

* Avoid overwriting next.config.js when content has not changed

Windows cannot seem to detect when the file changes but the
content is the same and triggers a much longer hot-reload
cycle to deal with the "updated next.config.js"

* Handle case where `next.config.js` doesn't exist yet
Masen Furer hace 1 año
padre
commit
82279b4f1a
Se han modificado 2 ficheros con 35 adiciones y 33 borrados
  1. 6 4
      reflex/utils/prerequisites.py
  2. 29 29
      reflex/utils/processes.py

+ 6 - 4
reflex/utils/prerequisites.py

@@ -568,15 +568,17 @@ def update_next_config(export=False, transpile_packages: Optional[List[str]] = N
         export: if the method run during reflex export.
         transpile_packages: list of packages to transpile via next.config.js.
     """
-    next_config_file = os.path.join(constants.Dirs.WEB, constants.Next.CONFIG_FILE)
+    next_config_file = Path(constants.Dirs.WEB, constants.Next.CONFIG_FILE)
 
     next_config = _update_next_config(
         get_config(), export=export, transpile_packages=transpile_packages
     )
 
-    with open(next_config_file, "w") as file:
-        file.write(next_config)
-        file.write("\n")
+    # Overwriting the next.config.js triggers a full server reload, so make sure
+    # there is actually a diff.
+    orig_next_config = next_config_file.read_text() if next_config_file.exists() else ""
+    if orig_next_config != next_config:
+        next_config_file.write_text(next_config)
 
 
 def _update_next_config(

+ 29 - 29
reflex/utils/processes.py

@@ -202,13 +202,19 @@ def run_concurrently(*fns: Union[Callable, Tuple]) -> None:
         pass
 
 
-def stream_logs(message: str, process: subprocess.Popen, progress=None):
+def stream_logs(
+    message: str,
+    process: subprocess.Popen,
+    progress=None,
+    suppress_errors: bool = False,
+):
     """Stream the logs for a process.
 
     Args:
         message: The message to display.
         process: The process.
         progress: The ongoing progress bar if one is being used.
+        suppress_errors: If True, do not exit if errors are encountered (for fallback).
 
     Yields:
         The lines of the process output.
@@ -232,7 +238,7 @@ def stream_logs(message: str, process: subprocess.Popen, progress=None):
     # Windows uvicorn bug
     # https://github.com/reflex-dev/reflex/issues/2335
     accepted_return_codes = [0, -2, 15] if constants.IS_WINDOWS else [0, -2]
-    if process.returncode not in accepted_return_codes:
+    if process.returncode not in accepted_return_codes and not suppress_errors:
         console.error(f"{message} failed with exit code {process.returncode}")
         for line in logs:
             console.error(line, end="")
@@ -251,15 +257,16 @@ def show_logs(message: str, process: subprocess.Popen):
         pass
 
 
-def show_status(message: str, process: subprocess.Popen):
+def show_status(message: str, process: subprocess.Popen, suppress_errors: bool = False):
     """Show the status of a process.
 
     Args:
         message: The initial message to display.
         process: The process.
+        suppress_errors: If True, do not exit if errors are encountered (for fallback).
     """
     with console.status(message) as status:
-        for line in stream_logs(message, process):
+        for line in stream_logs(message, process, suppress_errors=suppress_errors):
             status.update(f"{message} {line}")
 
 
@@ -298,29 +305,22 @@ def run_process_with_fallback(args, *, show_status_message, fallback=None, **kwa
         fallback: The fallback command to run.
         kwargs: Kwargs to pass to new_process function.
     """
-
-    def execute_process(process):
-        if not constants.IS_WINDOWS:
-            show_status(show_status_message, process)
-        else:
-            process.wait()
-            if process.returncode != 0:
-                error_output = process.stderr if process.stderr else process.stdout
-                error_message = f"Error occurred during subprocess execution: {' '.join(args)}\n{error_output.read() if error_output else ''}"
-                # Only show error in debug mode.
-                if console.is_debug():
-                    console.error(error_message)
-
-                # retry with fallback command.
-                fallback_args = [fallback, *args[1:]] if fallback else None
-                console.warn(
-                    f"There was an error running command: {args}. Falling back to: {fallback_args}."
-                )
-                if fallback_args:
-                    process = new_process(fallback_args, **kwargs)
-                    execute_process(process)
-            else:
-                show_status(show_status_message, process)
-
     process = new_process(args, **kwargs)
-    execute_process(process)
+    if fallback is None:
+        # No fallback given, or this _is_ the fallback command.
+        show_status(show_status_message, process)
+    else:
+        # Suppress errors for initial command, because we will try to fallback
+        show_status(show_status_message, process, suppress_errors=True)
+        if process.returncode != 0:
+            # retry with fallback command.
+            fallback_args = [fallback, *args[1:]]
+            console.warn(
+                f"There was an error running command: {args}. Falling back to: {fallback_args}."
+            )
+            run_process_with_fallback(
+                fallback_args,
+                show_status_message=show_status_message,
+                fallback=None,
+                **kwargs,
+            )