64

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?

Michael Currie
  • 11,166
  • 7
  • 39
  • 54
Tim McJilton
  • 5,634
  • 6
  • 22
  • 27
  • 1
    I'd say you should leave it uncorrected, since Alex already did it for you. It would make more sense to who is reading. – cregox May 13 '10 at 22:12
  • Cawas: I am going to add my initial uncorrected one back above it. Or something similar with the same errors. Good call – Tim McJilton May 13 '10 at 22:48
  • 1
    related: [Temporarily Redirect stdout/stderr](http://stackoverflow.com/q/6796492/4279) – jfs Jun 12 '15 at 19:24

8 Answers8

102

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;-).

Nick T
  • 22,202
  • 10
  • 72
  • 110
Alex Martelli
  • 762,786
  • 156
  • 1,160
  • 1,345
  • Alex Martelli: that last one seems too pretty. I never have used the "with" keyword before. I am going to have to look into it. – Tim McJilton May 13 '10 at 17:56
  • 12
    It should be noted that if you use this in a threaded environment, your substitution will apply to *all* threads. So if you use it in a threaded webserver for instance, stdout will get trashed for all threads (for the duration of that function call, which will be some random chunk of time in other threads). Handling this case is hard, and would involve `threading.local` (mostly I'd recommend try avoiding threads and stdout redirection). – Ian Bicking May 13 '10 at 21:12
  • I've downvoted this because it makes code hang — in such a way that it cannot be interrupted, as far as I can tell — if an exception is raised within the context. I think [@bitmous's answer](http://stackoverflow.com/a/40054132/1194883) is more robust *and* more elegant in a very clever way (no need for the `DummyFile` class). – Mike Jan 27 '17 at 17:18
  • After none of the answers in this thread worked for me, I found out after *way* too long that a C-level function was generating the output, which the python-stdout doesn't catch directly. The following answer helped: https://stackoverflow.com/a/17954769/2965879 In particular, `sys.stdout.close()` in the `try`-block should help. – Axel Apr 04 '18 at 15:37
  • @Alex Martelli I'm interested in your comment that one should import the whole module and then use qualified names. Can you point me to a explanation of that? The use of `from package import function` is certainly very common. – John Sep 29 '20 at 07:03
28

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):
                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.

vaultah
  • 36,713
  • 12
  • 105
  • 132
  • 2
    Most elegant solution for Python 3.4 and newer. – Dušan Maďar Jan 10 '18 at 16:09
  • It's a great way to suppress `openpyxl` with its `FutureWarning: The behavior of this method will change in future versions` nonsense. Only you have to use `contextlib.redirect_stderr` instead of `contextlib.redirect_stdout`. – z33k Mar 22 '20 at 10:57
16

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)
bitmous
  • 320
  • 3
  • 6
  • This is nice because it handles exceptions within the context. And the use of `self` as the trivial `write`r is very clever. – Mike Jan 27 '17 at 17:15
  • This is certainly the best answer. H̶i̶n̶t̶ ̶f̶o̶r̶ ̶a̶n̶y̶o̶n̶e̶ ̶u̶n̶s̶u̶r̶e̶:̶ ̶i̶f̶ ̶y̶o̶u̶ ̶w̶a̶n̶t̶ ̶e̶r̶r̶o̶r̶s̶ ̶t̶o̶ ̶b̶e̶ ̶t̶h̶r̶o̶w̶n̶ ̶a̶s̶ ̶n̶o̶r̶m̶a̶l̶,̶ ̶j̶u̶s̶t̶ ̶r̶e̶p̶l̶a̶c̶e̶ `# Do normal exception handling` ̶w̶i̶t̶h̶ ̶`raise`. I've made an edit – Anonymous Aug 03 '17 at 21:44
  • It should also be mentioned, here, that if you have any debugger traces set in `DoMyFunction()`, they will be invisible, the program will simply wait on the trace with no prompt. – Anonymous Aug 04 '17 at 16:34
  • 2
    What do you mean do normal exception handling here? What exactly will this catch? If I want to use this for any number of abstract cases, what should I be expecting to hit `if type is not None` and what happens if I omit that line? – Brendan Feb 24 '19 at 23:28
15

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
Elias Zamaria
  • 80,938
  • 29
  • 103
  • 136
Philipp
  • 43,805
  • 12
  • 78
  • 104
  • The only reason I think it is inefficient because it is a file write. File writes are definitely slower than needed if you are doing it enough times. But yeah I messed up my initial code, I fixed it based of Alex's initial comment – Tim McJilton May 13 '10 at 17:58
  • 4
    It should be `open(os.devnull, 'w')`. In general `os.devnull` is useful if you need *real* file object. But `print` accepts anything with a `write()` method. – jfs May 14 '10 at 07:58
  • 1
    +1 for os.devnull.. surprised the winning answer propagated the use of an actual file for output. – synthesizerpatel Sep 25 '12 at 01:26
  • 1
    Indeed it should be ```open(os.devnull, 'w')```. Otherwise you will get:[Errno 9] Bad file descriptor – danger89 Mar 11 '14 at 18:14
9

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()
Pin-Yen
  • 151
  • 1
  • 6
5

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)
Community
  • 1
  • 1
tgray
  • 8,110
  • 4
  • 34
  • 38
  • tgray: I really like that approach. Is it truly faster? It can save a little bit of time via not doing I guess two jmps and less pushing and pulling from the stack. That and I can over-call all the functions I will call. Is their another hidden advantage that I am not seeing? – Tim McJilton May 13 '10 at 20:10
  • Tim: It's main benefit is saving programmer time by reducing the number of changes you need to make to your code to turn off stdout for a function. I haven't run a performance test against the contextmanager yet. – tgray May 14 '10 at 14:57
  • tgray: Thats what I thought. It is a great Idea though. I do like it. I think both approaches are good depending on what you are attempting to do. IE if you are trying to replace a call in 100s of spot that way is better, but if you want to do it for a function being passed into another function, I think the other has its advantages. I do like it though – Tim McJilton May 14 '10 at 15:08
  • tgray: Random note. After timing the two ways wrapping a function that just does 1+1, because i only care to look at the overhead. That being said it was pretty much a wash. So which ever between the two styles is easiest to implement is the one to go for. – Tim McJilton May 14 '10 at 18:22
3

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
e-satis
  • 515,820
  • 103
  • 283
  • 322
3

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.

PatB
  • 355
  • 4
  • 12