A completed child can emit more data than a report contract permits, even when its exit status is zero.
Python subprocess output: spool bytes before accepting a report
Operation contract
A formatter writes to a temporary file, so the parent does not collect the entire stdout in a Python string. After the child exits, the owner measures bytes and rejects the oversized result before parsing it. The output limit is an acceptance boundary here, not a hard production disk quota. Pair a real untrusted command with an OS or streaming limit that stops growth while it runs.
Failure and ownership boundary
The child is a fixed local interpreter command. Never splice a requester-supplied shell string into it. A redirect to a file avoids an in-memory pipe buffer but consumes storage; reserve a spool directory with quotas and cleanup. stderr needs its own policy. A zero exit code only reports process success, not report validity.
Working program
import subprocess
import sys
import tempfile
with tempfile.TemporaryFile() as report_output:
completed = subprocess.run(
[sys.executable, "-c", "import sys; sys.stdout.write('R' * 47000)"],
stdout=report_output,
stderr=subprocess.DEVNULL,
check=True,
timeout=5,
)
report_output.seek(0, 2)
byte_count = report_output.tell()
print("exit", completed.returncode)
print("bytes", byte_count)
print("accepted", byte_count <= 4096)Output
exit 0
bytes 47000
accepted FalseCosts and limits
Spooling uses O(output bytes) temporary storage and O(1) application memory for the size check. The shown check occurs after completion; a live hard limit requires bounded streaming or platform quotas.
Common Mistakes
- Redirecting stdout does not enforce a disk budget.
- Do not treat returncode zero as proof that the output meets a schema.
- Discarded stderr hides diagnostics; use a bounded separate diagnostic path in a service.
Connected lessons
- Python subprocess: argument vectors, exit codes and bounded fixtures
- Python subprocess timeout: kill and reap the timed-out child
- Python bounded gzip decoding: cap expanded output before publishing it
Continue with Python subprocess pipe: stop after the first byte beyond budget.
