Redirecting stdio streams with a context manager
Steve D'Aprano
steve+python at pearwood.info
Fri Sep 29 13:06:37 EDT 2017
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.
More information about the Python-list
mailing list