Prevent redirect_stdout from swallowing a diagnostic after an exception
To prevent a redirected test diagnostic from being swallowed after an exception, put redirect_stdout in a with statement and catch the exception outside that block. Its exit method restores sys.stdout while the exception unwinds, before the outer handler emits the diagnostic.
This fixture saves the original stream only for an identity comparison; it never prints its representation. Inside redirect_stdout(captured), it prints "inside redirect" and raises RuntimeError. The outer except assigns the diagnostic after the redirect context has exited. The assertions check two concrete observations: sys.stdout is saved_stdout, and the capture contains exactly one newline-terminated line. The three displayed lines are consequently written after restoration, while the body’s line remains in captured.
A manual assignment such as sys.stdout = captured needs a carefully placed finally block to achieve the same restoration guarantee. Prefer the context manager for a bounded redirection in a small test or utility script. redirect_stdout changes process-global state, so the assertion here does not make it suitable for general library code, concurrent code, or subprocess output.
contextlib.redirect_stdout was added in Python 3.4. Its scope and global-state limitation are documented in the official Python reference.
AI assistance disclosure: This article was drafted with AI assistance and checked using the deterministic fixture below.
from contextlib import redirect_stdout
from io import StringIO
import sys
saved_stdout = sys.stdout
captured = StringIO()
try:
with redirect_stdout(captured):
print("inside redirect")
raise RuntimeError("test failure")
except RuntimeError:
diagnostic = "caught outside"
assert sys.stdout is saved_stdout
assert captured.getvalue() == "inside redirect\n"
print("restored: True")
print("captured: inside redirect")
print(f"diagnostic: {diagnostic}")
restored: True
captured: inside redirect
diagnostic: caught outside