Suppress stdout / stderr print from Python functions

前端 未结 9 1811
情歌与酒
情歌与酒 2020-11-29 06:59

I have a Python script that is using some closed-box Python functions (i.e. I can\'t edit these functions) provided by my employer. When I call these functions, they are pri

相关标签:
9条回答
  • 2020-11-29 07:32

    Did you try to redirect stderr too? e.g.

    sys.stdout = StringIO()
    sys.stderr = StringIO()
    foo(bar)
    sys.stdout = sys.__stdout__ # These are provided by python
    sys.stderr = sys.__stderr__
    

    Also using StringIO might use extra memory. You can use a dummy device instead (e.g. http://coreygoldberg.blogspot.com/2009/05/python-redirect-or-turn-off-stdout-and.html).

    0 讨论(0)
  • 2020-11-29 07:35

    My solution is similar to yours but uses contextlib and is a little shorter and easier to understand (IMHO).

    import contextlib
    
    
    @contextlib.contextmanager
    def stdchannel_redirected(stdchannel, dest_filename):
        """
        A context manager to temporarily redirect stdout or stderr
    
        e.g.:
    
    
        with stdchannel_redirected(sys.stderr, os.devnull):
            if compiler.has_function('clock_gettime', libraries=['rt']):
                libraries.append('rt')
        """
    
        try:
            oldstdchannel = os.dup(stdchannel.fileno())
            dest_file = open(dest_filename, 'w')
            os.dup2(dest_file.fileno(), stdchannel.fileno())
    
            yield
        finally:
            if oldstdchannel is not None:
                os.dup2(oldstdchannel, stdchannel.fileno())
            if dest_file is not None:
                dest_file.close()
    

    The context for why I created this is at this blog post. Similar to yours I think.

    I use it like this in a setup.py:

    with stdchannel_redirected(sys.stderr, os.devnull):
        if compiler.has_function('clock_gettime', libraries=['rt']):
            libraries.append('rt')
    
    0 讨论(0)
  • 2020-11-29 07:36

    I use a decorator for this. It saves sys.stdout and sys.stderr references and makes these variables point to null. Then, after the function execution the original references are retrieved. It is important to note the try/except block, that allows the retrieval of the original references even when an exception is raised on the function.

    def suppress_std(func):
        def wrapper(*args, **kwargs):
            stderr_tmp = sys.stderr
            stdout_tmp = sys.stdout
            null = open(os.devnull, 'w')
            sys.stdout = null
            sys.stderr = null
            try:
                result = func(*args, **kwargs)
                sys.stderr = stderr_tmp
                sys.stdout = stdout_tmp
                return result
            except:
                sys.stderr = stderr_tmp
                sys.stdout = stdout_tmp
                raise
        return wrapper
    

    To use:

    @suppress_std
    def function_std_suppressed():
        # code here
    
    0 讨论(0)
  • 2020-11-29 07:41

    Not really requested by the OP, but I needed to hide and store the output, and did like follows:

    from io import StringIO
    import sys
    
    class Hider:
        def __init__(self, channels=('stdout',)):
            self._stomach = StringIO()
            self._orig = {ch : None for ch in channels}
    
        def __enter__(self):
            for ch in self._orig:
                self._orig[ch] = getattr(sys, ch)
                setattr(sys, ch, self)
            return self
    
        def write(self, string):
            self._stomach.write(string)
    
        def flush(self):
            pass
    
        def autopsy(self):
            return self._stomach.getvalue()
    
        def __exit__(self, *args):
            for ch in self._orig:
                setattr(sys, ch, self._orig[ch])
    

    Usage:

    with Hider() as h:
        spammy_function()
        result = h.autopsy()
    

    (tested only with Python 3)

    EDIT: now allows to select stderr, stdout or both, as in Hider([stdout, stderr])

    0 讨论(0)
  • 2020-11-29 07:43

    As of python 3.5 we can do this with minimal work using built-ins in contextlib, namely redirect_stdout and redirect_stderr. We only need to combine these two built-in context managers in a custom context manager of ours, which can be easily done using the nice pattern in Martijn's answer here. Redirecting both outputs to os.devnull should be safe and portable enough.

    from contextlib import contextmanager,redirect_stderr,redirect_stdout
    from os import devnull
    
    @contextmanager
    def suppress_stdout_stderr():
        """A context manager that redirects stdout and stderr to devnull"""
        with open(devnull, 'w') as fnull:
            with redirect_stderr(fnull) as err, redirect_stdout(fnull) as out:
                yield (err, out)
    

    Note that suppressing stderr will still give you full tracebacks when something breaks, which is a good thing:

    import sys
    
    def rogue_function():
        print('spam to stdout')
        print('important warning', file=sys.stderr)
        1 + 'a'
        return 42
    
    with suppress_stdout_stderr():
        rogue_function()
    

    When run the above only prints

    Traceback (most recent call last):
      File "tmp.py", line 20, in <module>
        rogue_function()
      File "foo.py", line 16, in rogue_function
        1 + 'a'
    TypeError: unsupported operand type(s) for +: 'int' and 'str'
    

    to the terminal. Unhandled exceptions should never go unnoticed.

    0 讨论(0)
  • 2020-11-29 07:45

    Just use Linux/Unix:

    ./myscript.py 2>/dev/null # gets rid of stderr
    ./myscript.py 2>/somewhere/myerror.log
    
    0 讨论(0)
提交回复
热议问题