Skip to content

Commit

Permalink
Simplify run_process_with_fallback logic (#3089)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
masenf committed Apr 15, 2024
1 parent 3c8c7c3 commit 4aadbd8
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 33 deletions.
10 changes: 6 additions & 4 deletions reflex/utils/prerequisites.py
Expand Up @@ -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(
Expand Down
58 changes: 29 additions & 29 deletions reflex/utils/processes.py
Expand Up @@ -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.
Expand All @@ -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="")
Expand All @@ -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}")


Expand Down Expand Up @@ -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,
)

0 comments on commit 4aadbd8

Please sign in to comment.