summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/contextlib.rst31
-rw-r--r--Lib/contextlib.py40
-rw-r--r--Lib/test/test_contextlib.py9
-rw-r--r--Misc/NEWS2
4 files changed, 81 insertions, 1 deletions
diff --git a/Doc/library/contextlib.rst b/Doc/library/contextlib.rst
index 349b805..4b86755 100644
--- a/Doc/library/contextlib.rst
+++ b/Doc/library/contextlib.rst
@@ -115,6 +115,37 @@ Functions and classes provided:
.. versionadded:: 3.4
+.. function:: redirect_stdout(new_target)
+
+ Context manager for temporarily redirecting :data:`sys.stdout` to
+ another file or file-like object.
+
+ This tool adds flexibility to existing functions or classes whose output
+ is hardwired to stdout.
+
+ For example, the output of :func:`help` normally is sent to *sys.stdout*.
+ You can capture that output in a string by redirecting the output to a
+ :class:`io.StringIO` object::
+
+ f = io.StringIO()
+ with redirect_stdout(f):
+ help(pow)
+ s = f.getvalue()
+
+ To send the output of :func:`help` to a file on disk, redirect the output
+ to a regular file::
+
+ with open('help.txt', 'w') as f:
+ with redirect_stdout(f):
+ help(pow)
+
+ To send the output of :func:`help` to *sys.stderr*::
+
+ with redirect_stdout(sys.stderr):
+ help(pow)
+
+ .. versionadded:: 3.4
+
.. class:: ContextDecorator()
A base class that enables a context manager to also be used as a decorator.
diff --git a/Lib/contextlib.py b/Lib/contextlib.py
index aaab095..868fa6c 100644
--- a/Lib/contextlib.py
+++ b/Lib/contextlib.py
@@ -4,7 +4,8 @@ import sys
from collections import deque
from functools import wraps
-__all__ = ["contextmanager", "closing", "ContextDecorator", "ExitStack", "ignored"]
+__all__ = ["contextmanager", "closing", "ContextDecorator", "ExitStack",
+ "ignored", "redirect_stdout"]
class ContextDecorator(object):
@@ -140,6 +141,43 @@ class closing(object):
def __exit__(self, *exc_info):
self.thing.close()
+class redirect_stdout:
+ """Context manager for temporarily redirecting stdout to another file
+
+ # How to send help() to stderr
+
+ with redirect_stdout(sys.stderr):
+ help(dir)
+
+ # How to write help() to a file
+
+ with open('help.txt', 'w') as f:
+ with redirect_stdout(f):
+ help(pow)
+
+ # How to capture disassembly to a string
+
+ import dis
+ import io
+
+ f = io.StringIO()
+ with redirect_stdout(f):
+ dis.dis('x**2 - y**2')
+ s = f.getvalue()
+
+ """
+
+ def __init__(self, new_target):
+ self.new_target = new_target
+
+ def __enter__(self):
+ self.old_target = sys.stdout
+ sys.stdout = self.new_target
+ return self.new_target
+
+ def __exit__(self, exctype, excinst, exctb):
+ sys.stdout = self.old_target
+
@contextmanager
def ignored(*exceptions):
"""Context manager to ignore specified exceptions
diff --git a/Lib/test/test_contextlib.py b/Lib/test/test_contextlib.py
index 2892917..d8a0530 100644
--- a/Lib/test/test_contextlib.py
+++ b/Lib/test/test_contextlib.py
@@ -1,5 +1,6 @@
"""Unit tests for contextlib.py, and other context managers."""
+import io
import sys
import tempfile
import unittest
@@ -653,6 +654,14 @@ class TestIgnored(unittest.TestCase):
with ignored(LookupError):
'Hello'[50]
+class TestRedirectStdout(unittest.TestCase):
+
+ def test_redirect_to_string_io(self):
+ f = io.StringIO()
+ with redirect_stdout(f):
+ help(pow)
+ s = f.getvalue()
+ self.assertIn('pow', s)
if __name__ == "__main__":
unittest.main()
diff --git a/Misc/NEWS b/Misc/NEWS
index 4d78be6..04132c6 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -32,6 +32,8 @@ Library
- Issue #19158: a rare race in BoundedSemaphore could allow .release() too
often.
+- Issue #15805: Add contextlib.redirect_stdout().
+
- Issue #18716: Deprecate the formatter module.
- Issue #18037: 2to3 now escapes '\u' and '\U' in native strings.