subprocess
subprocess spawns external programs, captures stdout/stderr, and manages exit codes. Prefer argument lists without shell unless shell features are explicitly required.
Search across all documentation pages
subprocess spawns external programs, captures stdout/stderr, and manages exit codes. Prefer argument lists without shell unless shell features are explicitly required.
import subprocess
result = subprocess.run(
["python", "--version"],
capture_output=True,
text=True,
check=True,
)
print(result.stdout.strip())When to reach for this:
import subprocess
from pathlib import Path
def run_git(args: list[str], cwd: Path) -> str:
completed = subprocess.run(
["git", *args],
cwd=cwd,
capture_output=True,
text=True,
check=False,
)
if completed.returncode != 0:
raise RuntimeError(completed.stderr.strip() or "git failed")
return completed.stdout.strip()
def safe_ls(path: Path) -> str:
# Never shell=True with user input
proc = subprocess.run(
["ls", "-la", str(path)],
capture_output=True,
text=True,
timeout=5,
)
proc.check_returncode()
return proc.stdout
print(run_git(["--version"], Path.cwd()))What this demonstrates:
check_returncode or check=True for failurestimeout prevents hung childrentext=True decodes bytes to str with encoding default| API | Use |
|---|---|
run | One-shot wait for completion |
Popen | Streaming IO, background |
capture_output=True pipes stdout/stderrstdin=subprocess.DEVNULL when no inputshlex.quote if shell required.cwd and env.asyncio.create_subprocess_exec.| Alternative | Use When | Don't Use When |
|---|---|---|
| shutil.which | Resolve binary path | Need not run |
| asyncio subprocess | Async streaming | Sync script |
| Pure Python lib | Stable API exists | One-off CLI ok |
Only trusted fixed commands in controlled scripts - never with external input.
Success convention - check explicitly even if not using check=True.
stderr=subprocess.STDOUT into stdout pipe.
Omit text=True; handle bytes and decode explicitly.
creationflags for new console; list args still preferred.
env={**os.environ, "K": "V"} partial override.
Popen + communicate with timeout or poll loop.
Flag any shell=True and string concatenation in security review.
["uv", "run", "python", "-m", "tool"] in list form for reproducible env.
Use fixtures with tmp_path; mock subprocess.run for unit tests.
Stack versions: This page was written for Python 3.14.0 (stable 3.14, maintenance 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, and uv 0.6+.
Reviewed by Chris St. John·Last updated Jul 19, 2026