Silence the stdout of a function in Python without trashing sys.stdout and restoring each function call

Question:

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?

Asked By: Tim McJilton

||

Answers:

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

Answered By: Alex Martelli

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
Answered By: Philipp

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)
Answered By: tgray

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
Answered By: e-satis

I don’t think it gets any cleaner or faster than this;-)

Bah! I think I can do a little better 😀

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.

Answered By: PatB

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.

Answered By: vaultah

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)
Answered By: bitmous

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()
Answered By: Pin-Yen
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.