I have a Python script that is using some closed-box Python functions (i.e. I can\'t edit these functions) provided by my employer. When I call these functions, they are pri
Did you try to redirect stderr too? e.g.
sys.stdout = StringIO()
sys.stderr = StringIO()
foo(bar)
sys.stdout = sys.__stdout__ # These are provided by python
sys.stderr = sys.__stderr__
Also using StringIO might use extra memory. You can use a dummy device instead (e.g. http://coreygoldberg.blogspot.com/2009/05/python-redirect-or-turn-off-stdout-and.html).
My solution is similar to yours but uses contextlib
and is a little shorter and easier to understand (IMHO).
import contextlib
@contextlib.contextmanager
def stdchannel_redirected(stdchannel, dest_filename):
"""
A context manager to temporarily redirect stdout or stderr
e.g.:
with stdchannel_redirected(sys.stderr, os.devnull):
if compiler.has_function('clock_gettime', libraries=['rt']):
libraries.append('rt')
"""
try:
oldstdchannel = os.dup(stdchannel.fileno())
dest_file = open(dest_filename, 'w')
os.dup2(dest_file.fileno(), stdchannel.fileno())
yield
finally:
if oldstdchannel is not None:
os.dup2(oldstdchannel, stdchannel.fileno())
if dest_file is not None:
dest_file.close()
The context for why I created this is at this blog post. Similar to yours I think.
I use it like this in a setup.py
:
with stdchannel_redirected(sys.stderr, os.devnull):
if compiler.has_function('clock_gettime', libraries=['rt']):
libraries.append('rt')
I use a decorator for this. It saves sys.stdout
and sys.stderr
references and makes these variables point to null. Then, after the function execution the original references are retrieved. It is important to note the try/except block, that allows the retrieval of the original references even when an exception is raised on the function.
def suppress_std(func):
def wrapper(*args, **kwargs):
stderr_tmp = sys.stderr
stdout_tmp = sys.stdout
null = open(os.devnull, 'w')
sys.stdout = null
sys.stderr = null
try:
result = func(*args, **kwargs)
sys.stderr = stderr_tmp
sys.stdout = stdout_tmp
return result
except:
sys.stderr = stderr_tmp
sys.stdout = stdout_tmp
raise
return wrapper
To use:
@suppress_std
def function_std_suppressed():
# code here
Not really requested by the OP, but I needed to hide and store the output, and did like follows:
from io import StringIO
import sys
class Hider:
def __init__(self, channels=('stdout',)):
self._stomach = StringIO()
self._orig = {ch : None for ch in channels}
def __enter__(self):
for ch in self._orig:
self._orig[ch] = getattr(sys, ch)
setattr(sys, ch, self)
return self
def write(self, string):
self._stomach.write(string)
def flush(self):
pass
def autopsy(self):
return self._stomach.getvalue()
def __exit__(self, *args):
for ch in self._orig:
setattr(sys, ch, self._orig[ch])
Usage:
with Hider() as h:
spammy_function()
result = h.autopsy()
(tested only with Python 3)
EDIT: now allows to select stderr
, stdout
or both, as in Hider([stdout, stderr])
As of python 3.5 we can do this with minimal work using built-ins in contextlib, namely redirect_stdout and redirect_stderr. We only need to combine these two built-in context managers in a custom context manager of ours, which can be easily done using the nice pattern in Martijn's answer here. Redirecting both outputs to os.devnull should be safe and portable enough.
from contextlib import contextmanager,redirect_stderr,redirect_stdout
from os import devnull
@contextmanager
def suppress_stdout_stderr():
"""A context manager that redirects stdout and stderr to devnull"""
with open(devnull, 'w') as fnull:
with redirect_stderr(fnull) as err, redirect_stdout(fnull) as out:
yield (err, out)
Note that suppressing stderr
will still give you full tracebacks when something breaks, which is a good thing:
import sys
def rogue_function():
print('spam to stdout')
print('important warning', file=sys.stderr)
1 + 'a'
return 42
with suppress_stdout_stderr():
rogue_function()
When run the above only prints
Traceback (most recent call last):
File "tmp.py", line 20, in <module>
rogue_function()
File "foo.py", line 16, in rogue_function
1 + 'a'
TypeError: unsupported operand type(s) for +: 'int' and 'str'
to the terminal. Unhandled exceptions should never go unnoticed.
Just use Linux/Unix:
./myscript.py 2>/dev/null # gets rid of stderr
./myscript.py 2>/somewhere/myerror.log