Skip to content

Instantly share code, notes, and snippets.

@msabramo
Created Jul 20, 2013
Embed
What would you like to do?
An experiment with creating a context manager that can temporarily redirect stdout and stderr - for http://bugs.python.org/issue15805
import contextlib
import os
import sys
@contextlib.contextmanager
def redirected(**kwargs):
"""
A context manager to temporarily redirect stdout or stderr
Examples:
# Redirect stdout to /dev/null
>>> with redirected(stdout=None):
... os.system("echo foo; ls dfkdjfdkfd")
...
ls: dfkdjfdkfd: No such file or directory
# Redirect stderr to /dev/null
>>> with redirected(stderr=None):
... os.system("echo foo; ls djdffdd")
...
foo
256
# Redirect stdout and stderr to /dev/null
>>> with redirected(stdout=None, stderr=None):
... os.system("echo foo; ls djdffdd")
...
# Redirect stdout and stderr to filenames
>>> with redirected(stdout='stuff_stdout.txt', stderr='stuff_stderr.txt'):
... ret = os.system("echo foo; ls dfjkdfd")
...
>>> open('stuff_stdout.txt').read()
'foo\n'
>>> open('stuff_stderr.txt').read()
'ls: dfjkdfd: No such file or directory\n'
# Redirect stdout to a file and stderr to a filename
>>> with open('going_to_stdout.txt', 'w') as out:
... with redirected(stdout=out, stderr='stuff_stderr.txt'):
... ret = os.system("echo stuff going to stdout; ls 123456")
...
>>> open('going_to_stdout.txt', 'r').read()
'stuff going to stdout\n'
>>> open('stuff_stderr.txt', 'r').read()
'ls: 123456: No such file or directory\n'
# Redirect stdout to a file-like object
# This does not work with subprocesses.
>>> from StringIO import StringIO
>>> out = StringIO()
>>> with redirected(stdout=out):
... print('printing some stuff')
...
>>> out.getvalue()
'printing some stuff\n'
"""
dest_files = {}
old_filenos = {}
try:
for channel_name, destination in kwargs.items():
stdchannel = getattr(sys, channel_name)
old_filenos[channel_name] = os.dup(stdchannel.fileno())
dest_file = None
if destination is None:
dest_file = open(os.devnull, 'w')
elif hasattr(destination, 'startswith'): # A string => treat as filename
dest_file = open(destination, 'w')
elif hasattr(destination, 'fileno'): # A file-like object
dest_file = destination
else:
setattr(sys, channel_name, destination)
if dest_file:
os.dup2(dest_file.fileno(), stdchannel.fileno())
dest_files[channel_name] = dest_file
yield
finally:
for channel_name, old_fileno in old_filenos.items():
setattr(sys, '%s' % channel_name, getattr(sys, '__%s__' % channel_name))
if channel_name in dest_files:
stdchannel = getattr(sys, channel_name)
dest_file = dest_files[channel_name]
if old_fileno is not None:
os.dup2(old_fileno, stdchannel.fileno())
if dest_file is not None:
dest_file.close()
@msabramo
Copy link
Author

msabramo commented Jul 20, 2013

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