8

I have an annoying problem when using machine learning library PyML. PyML uses libsvm to train the SVM classifier. The problem is that libsvm outputs some text to standard output. But because that is outside of Python I cannot intercept it. I tried using methods described in problem Silence the stdout of a function in Python without trashing sys.stdout and restoring each function call but none of those help.

Is there any way how to do this. Modifying PyML is not an option.

Community
  • 1
  • 1
Rok
  • 454
  • 6
  • 12
  • have you check it , maybe it write in sys.stderr and not sys.stdout !!! – mouad Nov 14 '10 at 17:39
  • related: [Redirect stdout to a file in Python?](http://stackoverflow.com/a/22434262/4279) -- the file is `os.devnull` in this case. – jfs May 06 '14 at 22:54

4 Answers4

10

Open /dev/null for writing, use os.dup() to copy stdout, and use os.dup2() to copy your open /dev/null to stdout. Use os.dup2() to copy your copied stdout back to the real stdout after.

devnull = open('/dev/null', 'w')
oldstdout_fno = os.dup(sys.stdout.fileno())
os.dup2(devnull.fileno(), 1)
makesomenoise()
os.dup2(oldstdout_fno, 1)
Reid
  • 1,703
  • 2
  • 16
  • 24
Ignacio Vazquez-Abrams
  • 699,552
  • 132
  • 1,235
  • 1,283
  • 'oldstdout = os.dup(sys.stdout)' throws a typeerror 'an integer is required' – Rok Nov 14 '10 at 17:49
  • 3
    figured it out, sys.stdout.fileno() and devnull.fileno() are required and it works after that, thanks!! – Rok Nov 14 '10 at 17:53
  • 2
    Shouldn't you close devnull at the end using `devnull.close()` or open it using the `with` statement? – MrUser Mar 09 '16 at 15:41
  • Reid, your answer still does not work for me. I needed a `os.fsync(devnull.fileno())` before the final dup2 To debug, try `import pybullet as pb` as the thing you want to silence :) – andrea Oct 30 '20 at 00:38
3

Dave Smith gave a wonderful answer to that on his blog. Basically, it wraps Ignacio's answer nicely:

def suppress_stdout():
    with open(os.devnull, "w") as devnull:
        old_stdout = sys.stdout
        sys.stdout = devnull
        try:  
            yield
        finally:
            sys.stdout = old_stdout

Now, you can surround any function that garbles unwanted noise into stdout like this:

print "You can see this"
with suppress_stdout():
    print "You cannot see this"
print "And you can see this again"

For Python 3 you can use:

from contextlib import contextmanager
import os
import sys

@contextmanager
def suppress_stdout():
    with open(os.devnull, "w") as devnull:
        old_stdout = sys.stdout
        sys.stdout = devnull
        try:  
            yield
        finally:
            sys.stdout = old_stdout
freya
  • 3
  • 1
Manu CJ
  • 1,839
  • 11
  • 22
  • These solutions can only suppress output produced within python. – Vic May 23 '19 at 02:04
  • Probably, yeah. Also, I found that this solution fails to suppress some python output, for example the TQDM progress bar manages to print to Stdout even when within this context, not sure why. Still, I found that for most cases in Python, it works. – Manu CJ May 23 '19 at 09:45
  • TQDM progressbar is printed to stderr by default to supress it you would also need to redirect `sys.stderr` – Majo Dec 11 '20 at 10:19
0

I had the same problem and fixed it like that:

from cStringIO import StringIO

def wrapped_svm_predict(*args):
    """Run :func:`svm_predict` with no *stdout* output."""
    so, sys.stdout = sys.stdout, StringIO()
    ret = svm_predict(*args)
    sys.stdout = so
    return ret
Oben Sonne
  • 9,604
  • 2
  • 35
  • 58
  • 1
    This will work if svm_predict is the output comes from python but not if it comes from a shared library that python calls, which I believe is what the OP was asking about. – Alex Flint Oct 17 '12 at 17:25
  • For me it worked using the Python bindings of *libsvm*. I guess for PyML the situation is similar. Stdout is a process property, i.e. my wrapping (as well as Ignacio's solution) perfectly works as long as external functionality is not used via subprocesses. It only would fail if someone opens `/dev/stdout` to write output to. – Oben Sonne Oct 18 '12 at 18:36
  • 1
    Which is exactly what most shared libraries do, which is why this solution will not work for most people. The above solution is going to be closer to what the requestor needs. – kamelkev Jul 01 '13 at 18:21
0

I had a similar problem with portaudio/PyAudio initialization. I started with Reid's answer, which worked. Although I needed to redirect stderr instead. So, here is an updated, cross-platform version that redirects both:

import sys, os

# hide diagnostic output
with open(os.devnull, 'w') as devnull:
    # suppress stdout
    orig_stdout_fno = os.dup(sys.stdout.fileno())
    os.dup2(devnull.fileno(), 1)
    # suppress stderr
    orig_stderr_fno = os.dup(sys.stderr.fileno())
    os.dup2(devnull.fileno(), 2)

    print('*** stdout should be hidden!  ****')
    print('*** stderr should be too!  ****',
          file=sys.stderr)

    os.dup2(orig_stdout_fno, 1)  # restore stdout
    os.dup2(orig_stderr_fno, 2)  # restore stderr

print('done.')

Should be easy to comment out a part you don't need.

Gringo Suave
  • 25,443
  • 6
  • 77
  • 69