Card 05/ 08
Facade in the Wild: Python's subprocess.run
One call over Popen, pipes, waiting and return codes.
Python's subprocess.run is a facade over the module's process machinery. Running a program and reading its output with the lower-level Popen class means creating the process with pipes, reading from them while waiting for it to end, and then reading the return code. run does all of that in one call and returns the results in one object. This card shows the two side by side.
import subprocess
import sys
command = [sys.executable, "-c", "print('converted 3 files')"]
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
stdout, stderr = process.communicate()
print("Popen:", stdout.strip(), "| return code", process.returncode)
result = subprocess.run(command, capture_output=True, text=True, check=True)
print("run: ", result.stdout.strip(), "| return code", result.returncode)Popen: converted 3 files | return code 0
run: converted 3 files | return code 0Both produce the same output and return code. The Popen version names the pipes, calls communicate to read them and wait, and reads returncode afterwards. The run version asks for captured text output and an exception on failure, and gets back a CompletedProcess with stdout and returncode on it. In CPython, run is written with Popen and communicate inside it, which is the shape of a facade: a simple method over the classes that do the work.
Popen is still public, and the documentation recommends it for what run does not cover, such as reading output while a long-running process is still going. That is the second half of the pattern: the common case gets one call, and the subsystem stays open for the case that needs it.
← → move through sections and cards