Improve error handling in run_command

This commit is contained in:
Matthew Honnibal 2020-09-20 16:20:57 +02:00
parent 554c9a2497
commit 889128e5c5
1 changed files with 28 additions and 15 deletions

View File

@ -659,8 +659,8 @@ def join_command(command: List[str]) -> str:
def run_command(
command: Union[str, List[str]],
*,
capture: bool = False,
stdin: Optional[Any] = None,
capture: bool=False,
) -> Optional[subprocess.CompletedProcess]:
"""Run a command on the command line as a subprocess. If the subprocess
returns a non-zero exit code, a system exit is performed.
@ -668,33 +668,46 @@ def run_command(
command (str / List[str]): The command. If provided as a string, the
string will be split using shlex.split.
stdin (Optional[Any]): stdin to read from or None.
capture (bool): Whether to capture the output.
capture (bool): Whether to capture the output and errors. If False,
the stdout and stderr will not be redirected, and if there's an error,
sys.exit will be called with the returncode. You should use capture=False
when you want to turn over execution to the command, and capture=True
when you want to run the command more like a function.
RETURNS (Optional[CompletedProcess]): The process object.
"""
if isinstance(command, str):
command = split_command(command)
cmd_list = split_command(command)
cmd_str = command
else:
cmd_list = command
cmd_str = " ".join(command)
try:
ret = subprocess.run(
command,
cmd_list,
env=os.environ.copy(),
input=stdin,
encoding="utf8",
check=True,
check=False,
stdout=subprocess.PIPE if capture else None,
stderr=subprocess.PIPE if capture else None,
stderr=subprocess.STDOUT if capture else None,
)
except FileNotFoundError:
# Indicates the *command* wasn't found, it's an error before the command
# is run.
raise FileNotFoundError(
Errors.E970.format(str_command=" ".join(command), tool=command[0])
Errors.E970.format(str_command=cmd_str, tool=cmd_list[0])
) from None
except subprocess.CalledProcessError as e:
# We don't want a duplicate traceback here so we're making sure the
# CalledProcessError isn't re-raised. We also print both the string
# message and the stderr, in case the error only has one of them.
print(e.stderr)
print(e)
sys.exit(1)
if ret.returncode != 0:
if ret.returncode != 0 and capture:
message = f"Error running command:\n\n{cmd_str}\n\n"
message += f"Subprocess exited with status {ret.returncode}"
if ret.stdout is not None:
message += f"\n\nProcess log (stdout and stderr):\n\n"
message += ret.stdout
error = subprocess.SubprocessError(message)
error.ret = ret
error.command = cmd_str
raise error
elif ret.returncode != 0:
sys.exit(ret.returncode)
return ret