1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56 | #!/usr/bin/python
from __future__ import with_statement
import sys
from StringIO import StringIO
__all__ = ['RedirectedIO', 'redirect_io']
class RedirectedIO(object):
def __init__(self, target=None, mode='a+',
close_target=True):
try:
target = open(target, mode)
except TypeError:
if target is None:
target = StringIO()
self.target = target
self.close_target = close_target
def __enter__(self):
""" Redirect IO to self.target.
"""
self.original_stdout = sys.stdout
sys.stdout = self.target
return self.target
def __exit__(self, *args, **kwargs):
""" Restore stdio and close the file.
"""
sys.stdout = self.original_stdout
if self.close_target:
self.target.close()
def redirect_io(target=None, mode='a+', keep_target=True):
""" Returns a decorator that wrapps a
function and redirects its IO to a target
file (a StringIO by default). The target is
available as .iotarget on the decorated function.
"""
def dec(func):
def wrapper(*args, **kwargs):
with RedirectedIO(target, mode, not keep_target) as iotarget:
result = func(*args, **kwargs)
if keep_target:
wrapper.iotarget = iotarget
return result
wrapper.iotarget = None
wrapper.__doc__ = func.__doc__
wrapper.__name__ = func.__name__
return wrapper
return dec
|
You should restore the old stdout. Rather than setting sys.stdout to sys.__stdout__ in __exit__, you should save the old sys.stdout in an instance variable in __enter__, and restore it in __exit__. That way, if the user nests this construct, or redirects sys.stdout some other way, you won't clobber his change.
Nice point, thanks!
Typo in the __exit__ code: sys.stdout = sys.original_stdout. That should be self.original_stdout.