161

I have a large project consisting of sufficiently large number of modules, each printing something to the standard output. Now as the project has grown in size, there are large no. of print statements printing a lot on the std out which has made the program considerably slower.

So, I now want to decide at runtime whether or not to print anything to the stdout. I cannot make changes in the modules as there are plenty of them. (I know I can redirect the stdout to a file but even this is considerably slow.)

So my question is how do I redirect the stdout to nothing ie how do I make the print statement do nothing?

# I want to do something like this.
sys.stdout = None         # this obviously will give an error as Nonetype object does not have any write method.

Currently the only idea I have is to make a class which has a write method (which does nothing) and redirect the stdout to an instance of this class.

class DontPrint(object):
    def write(*args): pass

dp = DontPrint()
sys.stdout = dp

Is there an inbuilt mechanism in python for this? Or is there something better than this?

Pushpak Dagade
  • 6,280
  • 7
  • 28
  • 41
  • 1
    related: [Redirect stdout to a file in Python?](http://stackoverflow.com/q/4675728/4279) – jfs Mar 16 '14 at 08:50
  • 2
    I've just discovered a peculiar thing. Turning sys.stdout into None actually works in my program, although I'm not sure why. I was having *really* weird problems with encoding redirecting to os.devnull so I tried just using None, and it works. Might have something to do with me doing it in a Django unit test, but I'm not sure. – Teekin Jan 02 '18 at 01:33
  • @Teekin It does indeed work, and in contexts that have nothing to do with Django. – Carcigenicate Sep 13 '20 at 14:50
  • This question is a combination of [How to capture stdout output from a Python function call? - Stack Overflow](https://stackoverflow.com/questions/16571150/how-to-capture-stdout-output-from-a-python-function-call) and [Cross platform /dev/null in Python - Stack Overflow](https://stackoverflow.com/questions/2929899/cross-platform-dev-null-in-python), but there exists a **more efficient solution in this special case**, see below. – user202729 Nov 16 '22 at 09:55

15 Answers15

270

Cross-platform:

import os
import sys
f = open(os.devnull, 'w')
sys.stdout = f

On Windows:

f = open('nul', 'w')
sys.stdout = f

On Linux:

f = open('/dev/null', 'w')
sys.stdout = f
Community
  • 1
  • 1
Andrew Clark
  • 202,379
  • 35
  • 273
  • 306
  • 3
    How do you then cancel it? Is there a way to turn print statements back on after you are done? – jj172 Aug 16 '14 at 01:02
  • 8
    Sure, just save a reference to the original `sys.stdout` and set it back after. For example `old_stdout = sys.stdout` before any of the other code, then `sys.stdout = old_stdout` when you are done. – Andrew Clark Aug 20 '14 at 21:05
  • 1
    note: it doesn't redirect at a file descriptor level i.e., it may fail to redirect the output of external child processes, the output from C extensions that print to C stdout directly, `os.write(1, b'abc\n')`. You need `os.dup2()`, to redirect at the file descriptor level, see [`stdout_redirected()`](http://stackoverflow.com/a/22434262/4279) – jfs Mar 22 '16 at 17:36
  • 11
    Note there is `sys.__stdout__` for cancelling a redirection. So you just do `sys.stdout = sys.__stdout__` and no need to store the backup. – Konstantin Sekeresh May 01 '19 at 12:43
  • 1
    Note: Cleaner than `sys.stout = f` is `with contextlib.redirect_stdout(f):` [docs](https://docs.python.org/3/library/contextlib.html#contextlib.redirect_stdout) – Nearoo Dec 12 '21 at 10:33
50

A nice way to do this is to create a small context processor that you wrap your prints in. You then just use is in a with-statement to silence all output.

Python 2:

import os
import sys
from contextlib import contextmanager

@contextmanager
def silence_stdout():
    old_target = sys.stdout
    try:
        with open(os.devnull, "w") as new_target:
            sys.stdout = new_target
            yield new_target
    finally:
        sys.stdout = old_target

with silence_stdout():
    print("will not print")

print("this will print")

Python 3.4+:

Python 3.4 has a context processor like this built-in, so you can simply use contextlib like this:

import contextlib

with contextlib.redirect_stdout(None):
    print("will not print")

print("this will print")

If the code you want to surpress writes directly to sys.stdout using None as redirect target won't work. Instead you can use:

import contextlib
import sys
import os

with contextlib.redirect_stdout(open(os.devnull, 'w')):
    sys.stdout.write("will not print")

sys.stdout.write("this will print")

If your code writes to stderr instead of stdout, you can use contextlib.redirect_stderr instead of redirect_stdout.


Running this code only prints the second line of output, not the first:

$ python test.py
this will print

This works cross-platform (Windows + Linux + Mac OSX), and is cleaner than the ones other answers imho.

Emil Stenström
  • 13,329
  • 8
  • 53
  • 75
  • @celticminstrel you are totally right, thanks for correcting me. I've updated the code to reset sys.stdout afterwards. – Emil Stenström Sep 27 '16 at 19:29
  • This is missing some error checking, but great idea – Mad Physicist Jul 22 '18 at 23:25
  • `redirect_stdout(None)` doesn't work for me when I use it with output from `argparse`. But `redirect_stdout(NonWritable())` (see about it in this topic) works! – Nick Legend May 18 '21 at 18:50
  • @user10815638: I'm not sure how argparse is special. Could it be that it writes to stderr because it's an error? In that case there's redirect_stderr. – Emil Stenström May 20 '21 at 08:00
  • @Emil Stenström: Yes, `argparse` outputs to `sdderr` (not always) but with `contextlib.redirect_stderr(None)` it says: 'NoneType' object has no attribute 'write'. I published a bigger example down in this topic. – Nick Legend May 20 '21 at 20:18
  • @user10815638: Thanks for clarifying, and teaching me something new. Seems sys.stdout.write requires a proper write method on the redirection target stream, but print() does not. I've updated my example above to include a version that works when code is expecting a write method. – Emil Stenström May 21 '21 at 22:22
19

If you're in python 3.4 or higher, there's a simple and safe solution using the standard library:

import contextlib

with contextlib.redirect_stdout(None):
  print("This won't print!")
Lopsy
  • 136
  • 6
iFreilicht
  • 13,271
  • 9
  • 43
  • 74
8

(at least on my system) it appears that writing to os.devnull is about 5x faster than writing to a DontPrint class, i.e.

#!/usr/bin/python
import os
import sys
import datetime

ITER = 10000000
def printlots(out, it, st="abcdefghijklmnopqrstuvwxyz1234567890"):
   temp = sys.stdout
   sys.stdout = out
   i = 0
   start_t = datetime.datetime.now()
   while i < it:
      print st
      i = i+1
   end_t = datetime.datetime.now()
   sys.stdout = temp
   print out, "\n   took", end_t - start_t, "for", it, "iterations"

class devnull():
   def write(*args):
      pass


printlots(open(os.devnull, 'wb'), ITER)
printlots(devnull(), ITER)

gave the following output:

<open file '/dev/null', mode 'wb' at 0x7f2b747044b0> 
   took 0:00:02.074853 for 10000000 iterations
<__main__.devnull instance at 0x7f2b746bae18> 
   took 0:00:09.933056 for 10000000 iterations
DonP
  • 81
  • 1
  • 3
6

If you're in a Unix environment (Linux included), you can redirect output to /dev/null:

python myprogram.py > /dev/null

And for Windows:

python myprogram.py > nul
Steve Chambers
  • 37,270
  • 24
  • 156
  • 208
Nick Radford
  • 1,038
  • 6
  • 13
2

You can just mock it.

import mock

sys.stdout = mock.MagicMock()
Karthik Raja
  • 131
  • 2
  • 9
2

If you don't want to deal with resource-allocation nor rolling your own class, you may want to use TextIO from Python typing. It has all required methods stubbed for you by default.

import sys
from typing import TextIO

sys.stdout = TextIO()
Roy van Santen
  • 2,361
  • 3
  • 10
  • 11
1

How about this:

from contextlib import ExitStack, redirect_stdout
import os

with ExitStack() as stack:
    if should_hide_output():
        null_stream = open(os.devnull, "w")
        stack.enter_context(null_stream)
        stack.enter_context(redirect_stdout(null_stream))
    noisy_function()

This uses the features in the contextlib module to hide the output of whatever command you are trying to run, depending on the result of should_hide_output(), and then restores the output behavior after that function is done running.

If you want to hide standard error output, then import redirect_stderr from contextlib and add a line saying stack.enter_context(redirect_stderr(null_stream)).

The main downside it that this only works in Python 3.4 and later versions.

Elias Zamaria
  • 96,623
  • 33
  • 114
  • 148
  • 1
    It is easy to support [`redirect_stdout()`'s analog on earlier Python versions (e.g. using `contextlib.contextmanager`)](http://stackoverflow.com/a/22434262/4279). – jfs Mar 22 '16 at 17:33
1
sys.stdout = None

It is OK for print() case. But it can cause an error if you call any method of sys.stdout, e.g. sys.stdout.write().

There is a note in docs:

Under some conditions stdin, stdout and stderr as well as the original values stdin, stdout and stderr can be None. It is usually the case for Windows GUI apps that aren’t connected to a console and Python apps started with pythonw.

Alexander C
  • 3,597
  • 1
  • 23
  • 39
1

Supplement to iFreilicht's answer - it works for both python 2 & 3.

import sys

class NonWritable:
    def write(self, *args, **kwargs):
        pass

class StdoutIgnore:
    def __enter__(self):
        self.stdout_saved = sys.stdout
        sys.stdout = NonWritable()
        return self

    def __exit__(self, *args):
        sys.stdout = self.stdout_saved

with StdoutIgnore():
    print("This won't print!")
dizcza
  • 630
  • 1
  • 7
  • 19
1

Your class will work just fine (with the exception of the write() method name -- it needs to be called write(), lowercase). Just make sure you save a copy of sys.stdout in another variable.

If you're on a *NIX, you can do sys.stdout = open('/dev/null'), but this is less portable than rolling your own class.

Rafe Kettler
  • 75,757
  • 21
  • 156
  • 151
1

There are a number of good answers in the flow, but here is my Python 3 answer (when sys.stdout.fileno() isn't supported anymore) :

import os
import sys
oldstdout = os.dup(1)
oldstderr = os.dup(2)
oldsysstdout = sys.stdout
oldsysstderr = sys.stderr

# Cancel all stdout outputs (will be lost) - optionally also cancel stderr
def cancel_stdout(stderr=False):
    sys.stdout.flush()
    devnull = open('/dev/null', 'w')
    os.dup2(devnull.fileno(), 1)
    sys.stdout = devnull
    if stderr:
        os.dup2(devnull.fileno(), 2)
        sys.stderr = devnull

# Redirect all stdout outputs to a file - optionally also redirect stderr
def reroute_stdout(filepath, stderr=False):
    sys.stdout.flush()
    file = open(filepath, 'w')
    os.dup2(file.fileno(), 1)
    sys.stdout = file
    if stderr:
        os.dup2(file.fileno(), 2)
        sys.stderr = file

# Restores stdout to default - and stderr
def restore_stdout():
    sys.stdout.flush()
    sys.stdout.close()
    os.dup2(oldstdout, 1)
    os.dup2(oldstderr, 2)
    sys.stdout = oldsysstdout
    sys.stderr = oldsysstderr

To use it:

  • Cancel all stdout and stderr outputs with:

    cancel_stdout(stderr=True)

  • Route all stdout (but not stderr) to a file:

    reroute_stdout('output.txt')

  • To restore stdout and stderr:

    restore_stdout()

Muppet
  • 11
  • 2
0

Why don't you try this?

sys.stdout.close()
sys.stderr.close()
Veerendra K
  • 2,145
  • 7
  • 32
  • 61
0

Will add some example to the numerous answers here:

import argparse
import contextlib

class NonWritable:
    def write(self, *args, **kwargs):
        pass

parser = argparse.ArgumentParser(description='my program')
parser.add_argument("-p", "--param", help="my parameter", type=str, required=True)

#with contextlib.redirect_stdout(None): # No effect as `argparse` will output to `stderr`
#with contextlib.redirect_stderr(None): # AttributeError: 'NoneType' object has no attribute 'write'
with contextlib.redirect_stderr(NonWritable): # this works!
    args = parser.parse_args()

The normal output would be:

>python TEST.py
usage: TEST.py [-h] -p PARAM
TEST.py: error: the following arguments are required: -p/--param
Nick Legend
  • 789
  • 1
  • 7
  • 21
0

I use this. Redirect stdout to a string, which you subsequently ignore. I use a context manager to save and restore the original setting for stdout.

from io import StringIO
...
with StringIO() as out:
    with stdout_redirected(out):
        # Do your thing

where stdout_redirected is defined as:

from contextlib import contextmanager

@contextmanager
def stdout_redirected(new_stdout):
    save_stdout = sys.stdout
    sys.stdout = new_stdout
    try:
        yield None
    finally:
        sys.stdout = save_stdout

philhanna
  • 73
  • 1
  • 6