Skip to content

Instantly share code, notes, and snippets.

@msabramo
Last active December 20, 2015 01:29
Show Gist options
  • Save msabramo/6049140 to your computer and use it in GitHub Desktop.
Save msabramo/6049140 to your computer and use it in GitHub Desktop.
Experiment for Python issue 15805: An attempt at adding redirect_stdin, redirect_stdout, and redirect_stderr context manager functions to the `io` module
diff -r e7305517260b Lib/io.py
--- a/Lib/io.py Sat Jul 20 15:12:19 2013 +0200
+++ b/Lib/io.py Sun Jul 21 10:14:26 2013 -0700
@@ -56,6 +56,8 @@
BufferedWriter, BufferedRWPair, BufferedRandom,
IncrementalNewlineDecoder, TextIOWrapper)
+from unittest.mock import patch
+
OpenWrapper = _io.open # for compatibility with _pyio
# Pretend this exception was created here.
@@ -90,3 +92,64 @@
for klass in (StringIO, TextIOWrapper):
TextIOBase.register(klass)
del klass
+
+def redirect_stdin(replacement=None):
+ """Redirect stdin to come from a StringIO or str.
+
+ 'replacement' is a StringIO or str.
+
+ Examples:
+
+ >>> from io import redirect_stdin, StringIO
+ >>> with redirect_stdin(StringIO("bleargh")) as s:
+ ... x = input()
+ ...
+ >>> x
+ 'bleargh'
+
+ >>> with redirect_stdin("bleargh") as s:
+ ... x = input()
+ ...
+ >>> x
+ 'bleargh'
+ """
+ if hasattr(replacement, 'readline'): # a file-like object
+ file_like_obj = replacement
+ else:
+ # Try to convert to a file-like object; this works for strs at least
+ file_like_obj = StringIO(replacement)
+
+ return redirect_stdfile('sys.stdin', file_like_obj)
+
+def redirect_stdout(replacement=None):
+ """Redirect stdout to a StringIO.
+
+ Example:
+
+ >>> from io import redirect_stdout
+ >>> with redirect_stdout() as s:
+ ... print('hello')
+ ...
+ >>> s.getvalue()
+ 'hello\n'
+ """
+ return redirect_stdfile('sys.stdout', replacement)
+
+def redirect_stderr(replacement=None):
+ """Redirect stderr to a StringIO.
+
+ Example:
+
+ >>> import sys
+ >>> from io import redirect_stderr
+ >>> with redirect_stderr() as s:
+ ... print("bleargh", file=sys.stderr)
+ ...
+ >>> s.getvalue()
+ 'bleargh\n'
+ """
+ return redirect_stdfile('sys.stderr', replacement)
+
+def redirect_stdfile(stdfilename, replacement=None):
+ # stdfilename is one of ('sys.stdin', 'sys.stdout', 'sys.stderr')
+ return patch(stdfilename, replacement if replacement is not None else StringIO())
import sys
from io import redirect_stdin, redirect_stdout, redirect_stderr, StringIO
from tempfile import TemporaryFile
#----------------------------------------------------------------------------
# redirect_stdin with a StringIO as input
#----------------------------------------------------------------------------
with redirect_stdin(StringIO("bleargh")) as s:
foo = input()
print("captured stdin: %s" % s.getvalue())
#----------------------------------------------------------------------------
# redirect_stdin with a str as input
#----------------------------------------------------------------------------
with redirect_stdin("some text") as s:
foo = input()
print("captured stdin: %s" % s.getvalue())
#----------------------------------------------------------------------------
# redirect_stdin with a file as input
#----------------------------------------------------------------------------
with TemporaryFile(mode='r+') as f:
f.write("one\ntwo\nthree\n")
f.seek(0)
lst = []
with redirect_stdin(f):
for i in range(0, 3):
lst.append(input())
print('lst = %r' % lst)
# lst = ['one', 'two', 'three']
#----------------------------------------------------------------------------
# redirect_stdout captures stdout to a StringIO
#----------------------------------------------------------------------------
with redirect_stdout() as s:
sys.stdout.write("hello")
print("captured stdout: %s" % s.getvalue())
#----------------------------------------------------------------------------
# redirect_stderr captures stderr to a StringIO
#----------------------------------------------------------------------------
with redirect_stderr() as s:
sys.stderr.write("hello")
print("captured stderr: %s" % s.getvalue())
#----------------------------------------------------------------------------
# Using redirect_stdout and redirect_stdin together
# redirect_stdout suppresses the prompt from `input`
# redirect_stdin with a StringIO as input
#----------------------------------------------------------------------------
with redirect_stdout(), redirect_stdin(StringIO("bleargh")) as s:
foo = input("Type something> ")
print("captured stdin: %s" % s.getvalue())
@msabramo
Copy link
Author

Example output:

marca@marca-mac:~/dev/hg-repos/cpython$ DYLD_FRAMEWORK_PATH=. \
> ./python.exe test_redirect_issue_15805.py
captured stdin: bleargh
captured stdin: some text
lst = ['one', 'two', 'three']
captured stdout: hello
captured stderr: hello
captured stdin: bleargh

@msabramo
Copy link
Author

marca@marca-mac:~/dev/hg-repos/cpython$ hg tip
changeset:   84735:e7305517260b
tag:         tip
parent:      84733:9d93a267b8a0
parent:      84734:76bb3fe6ce8f
user:        Christian Heimes <[email protected]>
date:        Sat Jul 20 15:12:19 2013 +0200
files:       Modules/_elementtree.c
description:
Add missing check of PyDict_Update()'s return value in _elementtree.c
CID 719637

@msabramo
Copy link
Author

@msabramo
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment