Pushpak Dagade
Pushpak Dagade

Reputation: 6450

Redirecting stdout to "nothing" in python

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?

Upvotes: 166

Views: 137648

Answers (15)

s3dev
s3dev

Reputation: 9701

A bit late to the party, and wanted to share a recently discovered method, applicable to Python 3.8+.

Using os.devnull:

The question is how to 'send stdout to nothing'. Well, let's do just that, using /dev/null.

with contextlib.redirect_stdout(f := open(os.devnull, 'w')):
    ... # code continues
    f.close()

The named expression file handle (f) is assigned during the call to open, and thus can be closed at the end of the redirection context. Thanks to the fondly named, 'walrus operator'.

The key that separates this answer from others, is the ability to close the file handle responsibly and prevent the ResourceWarning and underlying file access issues.

Background:

The following method, while more simple, does work in some contexts. However, this had undesired side effects which ultimately resulted in unit tests failing; which lead to the implementation shown above.

with contextlib.redirect_stdout(None):
    ...  # code continues

Imports

import contextlib
import os

Upvotes: 0

philhanna
philhanna

Reputation: 85

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

Upvotes: 0

Muppet
Muppet

Reputation: 11

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

Upvotes: 1

Roy van Santen
Roy van Santen

Reputation: 2691

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

Upvotes: 2

Emil Stenström
Emil Stenström

Reputation: 14086

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.

Upvotes: 54

Nick Legend
Nick Legend

Reputation: 1038

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

Upvotes: 0

dizcza
dizcza

Reputation: 698

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!")

Upvotes: 1

iFreilicht
iFreilicht

Reputation: 14474

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!")

Upvotes: 20

Karthik Raja
Karthik Raja

Reputation: 129

You can just mock it.

import mock

sys.stdout = mock.MagicMock()

Upvotes: 1

Alexander C
Alexander C

Reputation: 4027

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.

Upvotes: 1

Elias Zamaria
Elias Zamaria

Reputation: 101073

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.

Upvotes: 1

Nick Radford
Nick Radford

Reputation: 1038

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

Upvotes: 6

Andrew Clark
Andrew Clark

Reputation: 208475

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

Upvotes: 278

DonP
DonP

Reputation: 81

(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

Upvotes: 8

Rafe Kettler
Rafe Kettler

Reputation: 76955

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.

Upvotes: 1

Related Questions