Scripts that ignore failed commands carry on with bad state; exceptions stop them at the real problem.
run does not raise when a command fails: a non-zero returncode is just a number on the result, and it's easy to forget to check. Pass check=True and a non-zero exit raises subprocess.CalledProcessError, which carries returncode, cmd, and the captured stdout / stderr (if you captured them).
try:
subprocess.run(["make", "test"], check=True, capture_output=True, text=True)
except subprocess.CalledProcessError as e:
print(e.returncode, e.stderr)Use check=True by default in scripts. Leave it off only when a non-zero code is an expected answer, for example grep returns 1 for "no match" and diff returns 1 for "files differ"; then inspect returncode yourself, or call result.check_returncode() later.
A different failure happens before the program even runs: if the executable doesn't exist, you get FileNotFoundError; if it isn't executable, PermissionError. Both are OSErrors. shutil.which("git") lets you check for a tool up front and give a friendly message.
Both CalledProcessError and TimeoutExpired inherit from subprocess.SubprocessError, handy for one except clause.
Example
import subprocess, sys
try:
subprocess.run([sys.executable, "-c", "import sys; print('boom', file=sys.stderr); sys.exit(2)"],
check=True, capture_output=True, text=True)
except subprocess.CalledProcessError as e:
print("exit", e.returncode, "stderr:", e.stderr.strip()) # exit 2 stderr: boom
try:
subprocess.run(["no-such-tool", "--version"])
except FileNotFoundError as e:
print("missing:", e.filename) # missing: no-such-tool
r = subprocess.run(["grep", "x"], input="abc\n", text=True, capture_output=True)
print("grep found nothing" if r.returncode == 1 else "match") # grep found nothingRemember
- run() never raises on non-zero exit unless check=True
- CalledProcessError carries returncode, stdout and stderr
- A missing program raises FileNotFoundError before anything runs
Watch out for
- Running a build step without check=True and continuing after it failed.