Timeouts

subprocess · Practical · 8 min

A hung network tool or stuck build should not hang your script or CI job forever.

run(..., timeout=seconds) waits at most that long. If the process is still running, run kills it, waits for it to exit, and raises subprocess.TimeoutExpired. The exception has cmd, timeout, and whatever output / stderr was captured so far (bytes, even with text=True, and possibly None).

try:
    subprocess.run(["curl", url], timeout=10, check=True)
except subprocess.TimeoutExpired:
    log.warning("curl took too long")

Notes:

  • The timeout covers the whole run, including reading output.
  • The kill is SIGKILL on POSIX, so the child gets no chance to clean up. For a graceful stop, use Popen and call terminate() first.
  • With shell=True, the shell is killed but grandchildren it started may keep running and holding the output pipes open, so run can still block after the timeout. Avoid the shell, or use a process group (an advanced lesson).

Pick timeouts generously enough for slow CI machines, and include the command and timeout in the error message so failures are easy to diagnose.

Example

import subprocess, sys, time

t = time.perf_counter()
try:
    subprocess.run([sys.executable, "-c", "import time; print('start', flush=True); time.sleep(30)"],
                   timeout=0.5, capture_output=True, text=True)
except subprocess.TimeoutExpired as e:
    print(f"killed after {e.timeout}s")            # killed after 0.5s
    print("partial output:", e.output)             # partial output: b'start\n'
print(f"elapsed {time.perf_counter() - t:.1f}s")   # elapsed 0.5s

Remember

  • timeout= kills the child and raises TimeoutExpired
  • Captured partial output is on the exception
  • Grandchildren of a shell may survive the kill

Watch out for

  • Expecting text output on TimeoutExpired; e.output is bytes even with text=True.

Check yourself

  1. What does run(cmd, timeout=5) do when cmd is still running after 5 seconds?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.python.org/3/library/subprocess.html#subprocess.TimeoutExpired