Redirecting stdio streams with a context manager
Peter Otten
__peter__ at web.de
Fri Sep 29 14:49:15 EDT 2017
Steve D'Aprano wrote:
> 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
That's the sane approach, but I just learned that you can reuse a single
instance:
>>> here = io.StringIO()
>>> there = io.StringIO()
>>> h = redirect_stdout(here)
>>> t = redirect_stdout(there)
>>> with h:
... print("ham")
... with t:
... print("foo")
... with h:
... print("spam")
... with t:
... print("bar")
...
>>> here.getvalue()
'ham\nspam\n'
>>> there.getvalue()
'foo\nbar\n'
More information about the Python-list
mailing list