Is there a way in Python to silence stdout without wrapping a function call like following?
Original Broken Code:
from sys import stdout
from copy import copy
save_stdout = copy(stdout)
stdout = open('trash','w')
foo()
stdout = save_stdout
Edit: Corrected code from Alex Martelli
import sys
save_stdout = sys.stdout
sys.stdout = open('trash', 'w')
foo()
sys.stdout = save_stdout
That way works but appears to be terribly inefficient. There has to be a better way. Any ideas?
suppress stdout and stderr with context managerUse suppress_stdout and suppress_stderr flags to indicate which stream to be suppressed. Save the state of the sys. stdout and sys. stderr in the __enter__ function, and redirect them to devnull based on the suppress_stdout and suppress_stderr flags.
stdout. A built-in file object that is analogous to the interpreter's standard output stream in Python. stdout is used to display output directly to the screen console.
In Python, whenever we use print() the text is written to Python's sys. stdout, whenever input() is used, it comes from sys. stdin, and whenever exceptions occur it is written to sys. stderr.
In IDLE, sys. __stdout__ is the program's original standard output - which goes nowhere, since it's not a console application. In other words, IDLE itself has already replaced sys. stdout with something else (its own console window), so you're taking two steps backwards by replacing your own stdout with __stdout__ .
Assigning the stdout
variable as you're doing has no effect whatsoever, assuming foo
contains print
statements -- yet another example of why you should never import stuff from inside a module (as you're doing here), but always a module as a whole (then use qualified names). The copy
is irrelevant, by the way. The correct equivalent of your snippet is:
import sys
save_stdout = sys.stdout
sys.stdout = open('trash', 'w')
foo()
sys.stdout = save_stdout
Now, when the code is correct, is the time to make it more elegant or fast. For example, you could use an in-memory file-like object instead of file 'trash':
import sys
import io
save_stdout = sys.stdout
sys.stdout = io.BytesIO()
foo()
sys.stdout = save_stdout
for elegance, a context is best, e.g:
import contextlib
import io
import sys
@contextlib.contextmanager
def nostdout():
save_stdout = sys.stdout
sys.stdout = io.BytesIO()
yield
sys.stdout = save_stdout
once you have defined this context, for any block in which you don't want a stdout,
with nostdout():
foo()
More optimization: you just need to replace sys.stdout with an object that has a no-op write
method. For example:
import contextlib
import sys
class DummyFile(object):
def write(self, x): pass
@contextlib.contextmanager
def nostdout():
save_stdout = sys.stdout
sys.stdout = DummyFile()
yield
sys.stdout = save_stdout
to be used the same way as the previous implementation of nostdout
. I don't think it gets any cleaner or faster than this;-).
Just to add to what others already said, Python 3.4 introduced the contextlib.redirect_stdout
context manager. It accepts a file(-like) object to which the output is to be redirected.
Redirecting to /dev/null will suppress the output:
In [11]: def f(): print('noise')
In [12]: import os, contextlib
In [13]: with open(os.devnull, 'w') as devnull:
....: with contextlib.redirect_stdout(devnull):
....: f()
....:
In [14]:
This solution can be adapted to be used as a decorator:
import os, contextlib
def supress_stdout(func):
def wrapper(*a, **ka):
with open(os.devnull, 'w') as devnull:
with contextlib.redirect_stdout(devnull):
return func(*a, **ka)
return wrapper
@supress_stdout
def f():
print('noise')
f() # nothing is printed
Another possible and occasionally useful solution that will work in both Python 2 and 3 is to pass /dev/null as an argument to f
and redirect the output using the file
argument of the print
function:
In [14]: def f(target): print('noise', file=target)
In [15]: with open(os.devnull, 'w') as devnull:
....: f(target=devnull)
....:
In [16]:
You can even make target
completely optional:
def f(target=sys.stdout):
# Here goes the function definition
Note, you'll need to
from __future__ import print_function
in Python 2.
Chiming in very late to this with what I thought was a cleaner solution to this problem.
import sys, traceback
class Suppressor(object):
def __enter__(self):
self.stdout = sys.stdout
sys.stdout = self
def __exit__(self, type, value, traceback):
sys.stdout = self.stdout
if type is not None:
# Do normal exception handling
def write(self, x): pass
Usage:
with Suppressor():
DoMyFunction(*args,**kwargs)
Why do you think this is inefficient? Did you test it? By the way, it does not work at all because you are using the from ... import
statement.
Replacing sys.stdout
is fine, but don't make a copy and don't use a temporary file. Open the null device instead:
import sys
import os
def foo():
print "abc"
old_stdout = sys.stdout
sys.stdout = open(os.devnull, "w")
try:
foo()
finally:
sys.stdout.close()
sys.stdout = old_stdout
redirect_stdout() has been added to contextlib since python 3.4
For python >= 3.4, this should do it:
import contextlib
import io
with contextlib.redirect_stdout(io.StringIO()):
foo()
A slight modification to Alex Martelli's answer...
This addresses the case where you always want to suppress stdout
for a function instead of individual calls to the function.
If foo()
was called many times would it might be better/easier to wrap the function (decorate it). This way you change the definition of foo
once instead of encasing every use of the function in a with-statement.
import sys
from somemodule import foo
class DummyFile(object):
def write(self, x): pass
def nostdout(func):
def wrapper(*args, **kwargs):
save_stdout = sys.stdout
sys.stdout = DummyFile()
func(*args, **kwargs)
sys.stdout = save_stdout
return wrapper
foo = nostdout(foo)
I don't think it gets any cleaner or faster than this;-)
Bah! I think I can do a little better :-D
import contextlib, cStringIO, sys
@contextlib.contextmanager
def nostdout():
'''Prevent print to stdout, but if there was an error then catch it and
print the output before raising the error.'''
saved_stdout = sys.stdout
sys.stdout = cStringIO.StringIO()
try:
yield
except Exception:
saved_output = sys.stdout
sys.stdout = saved_stdout
print saved_output.getvalue()
raise
sys.stdout = saved_stdout
Which gets to what I wanted originally, to suppress output normally but to show the suppressed output if an error was thrown.
By generalizing even more, you can get a nice decorator that can capture the ouput and even return it:
import sys
import cStringIO
from functools import wraps
def mute(returns_output=False):
"""
Decorate a function that prints to stdout, intercepting the output.
If "returns_output" is True, the function will return a generator
yielding the printed lines instead of the return values.
The decorator litterally hijack sys.stdout during each function
execution for ALL THE THREADS, so be careful with what you apply it to
and in which context.
>>> def numbers():
print "42"
print "1984"
...
>>> numbers()
42
1984
>>> mute()(numbers)()
>>> list(mute(True)(numbers)())
['42', '1984']
"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
saved_stdout = sys.stdout
sys.stdout = cStringIO.StringIO()
try:
out = func(*args, **kwargs)
if returns_output:
out = sys.stdout.getvalue().strip().split()
finally:
sys.stdout = saved_stdout
return out
return wrapper
return decorator
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With