85

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?

3
  • 2
    I'd say you should leave it uncorrected, since Alex already did it for you. It would make more sense to who is reading. Commented May 13, 2010 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 Commented May 13, 2010 at 22:48
  • 1
    related: Temporarily Redirect stdout/stderr Commented Jun 12, 2015 at 19:24

8 Answers 8

127

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

Sign up to request clarification or add additional context in comments.

7 Comments

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.
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).
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 is more robust and more elegant in a very clever way (no need for the DummyFile class).
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: stackoverflow.com/a/17954769/2965879 In particular, sys.stdout.close() in the try-block should help.
Doesn't work in 2022 with Py3.10 - BytesIO.write requires byes-like not str.
|
58

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]: 

If the wrapped code doesn't write to sys.stdout directly, you can use the simpler

In [15]: with contextlib.redirect_stdout(None): ....: f() ....: In [16]: 

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.

7 Comments

Most elegant solution for Python 3.4 and newer.
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.
+1 Notably, much of the time you can get away with just with contextlib.redirect_stdout(None), instead of messing with devnull. That doesn't work if the code inside the context manager directly uses sys.stdout; in that case you really do need devnull. But usually the simpler solution will suffice.
Maybe add functools.wrap to the supress_stdout decorator.
@Mike you should turn that into an answer or edit it into the answers, this is too good to be a comment
|
24

Chiming in very late to this with what I thought was a cleaner solution to this problem.

import sys, traceback class Suppressor(): def __enter__(self): self.stdout = sys.stdout sys.stdout = self def __exit__(self, exception_type, value, traceback): sys.stdout = self.stdout if exception_type is not None: # Do normal exception handling raise Exception(f"Got exception: {exception_type} {value} {traceback}") def write(self, x): pass def flush(self): pass 

Usage:

with Suppressor(): DoMyFunction(*args,**kwargs) 

5 Comments

This is nice because it handles exceptions within the context. And the use of self as the trivial writer is very clever.
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
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.
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?
Because of AttributeError: 'Suppressor' object has no attribute 'flush', I also had to add def flush(self): pass.
17

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() 

2 Comments

Is this a duplicate of @vaultah's answer above?
have my upvote, there's no fluff to wade through here,
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 

4 Comments

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
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.
+1 for os.devnull.. surprised the winning answer propagated the use of an actual file for output.
Indeed it should be open(os.devnull, 'w'). Otherwise you will get:[Errno 9] Bad file descriptor
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) 

4 Comments

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

Comments

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.

1 Comment

I was looking for a way to implement a sort of /q quiet switch in my python program where all output would be suppressed if the arg was called. Possible without being redundant?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.