In the standard library's contextlib.py module, there is a class for redirecting standard I/O streams, and two public functions. The code is short enough to reproduce here:
# From Python 3.5 class _RedirectStream: _stream = None def __init__(self, new_target): self._new_target = new_target # We use a list of old targets to make this CM re-entrant self._old_targets = [] def __enter__(self): self._old_targets.append(getattr(sys, self._stream)) setattr(sys, self._stream, self._new_target) return self._new_target def __exit__(self, exctype, excinst, exctb): setattr(sys, self._stream, self._old_targets.pop()) class redirect_stdout(_RedirectStream): # docstring removed _stream = "stdout" class redirect_stderr(_RedirectStream): # docstring removed _stream = "stderr" I don't understand the comment "We use a list of old targets to make this CM re-entrant". Under what circumstances will there ever be more than a single entry in _old_targets? If you use the context manager twice: with redirect_stdout(f1) as instance1: with redirect_stdout(f2) as instance2: pass the two calls will return different instances and sys.stdout will be set as follows: # before first call to redirect_stdout sys.stdout = __stdout__ # the original setting # first call __enter__ save __stdout__ in instance1._old_targets set sys.stdout = f1 # second call __enter__ save f1 in instance2._old_targets set sys.stdout = f2 # second call __exit__ restore sys.stdout = f1 # first call __exit__ restore sys.stdout = __stdout__ I'm not seeing why _old_targets is a list. Can anyone explain? -- Steve “Cheer up,” they said, “things could be worse.” So I cheered up, and sure enough, things got worse. -- https://mail.python.org/mailman/listinfo/python-list