I want to suppress all of the terminal output produced by a function that runs executables.
I have attempted to suppress the output of a Python function by using a context manager that temporarily redefines stdout and stderr each time the function is called. This suppresses terminal output produced by print
calls in the function, but it doesn't seem to work when the function calls executables that produce terminal output.
So, how could the output of executables called by Python functions be suppressed?
My code is below. I have included an example function that calls ls
to try to illustrate the kind of terminal output I want to suppress (though the function I'm dealing with is different).
#!/usr/bin/env python
import os
import subprocess
import sys
def main():
print("hello")
with silence():
print("there")
print("world")
with silence():
engage_command(command = "ls")
class silence(object):
def __init__(
self,
stdout = None,
stderr = None
):
if stdout == None and stderr == None:
devnull = open(os.devnull, "w")
stdout = devnull
stderr = devnull
self._stdout = stdout or sys.stdout
self._stderr = stderr or sys.stderr
def __enter__(
self
):
self.old_stdout = sys.stdout
self.old_stderr = sys.stderr
self.old_stdout.flush()
self.old_stderr.flush()
sys.stdout = self._stdout
sys.stderr = self._stderr
def __exit__(
self,
exc_type,
exc_value,
traceback
):
self._stdout.flush()
self._stderr.flush()
sys.stdout = self.old_stdout
sys.stderr = self.old_stderr
def engage_command(
command = None
):
process = subprocess.Popen(
[command],
shell = True,
executable = "/bin/bash")
process.wait()
output, errors = process.communicate()
return output
if __name__ == "__main__":
main()
In my particular case, I'm trying to run the following function (instead of the ls
function above):
with propyte.silence():
stream = pyaudio.PyAudio().open(
format = pyaudio.PyAudio().get_format_from_width(1),
channels = 1,
rate = bitrate,
output = True
)
When run, this produces output like the following:
ALSA lib pcm_dsnoop.c:606:(snd_pcm_dsnoop_open) unable to open slave
ALSA lib pcm_dmix.c:1029:(snd_pcm_dmix_open) unable to open slave
ALSA lib pcm.c:2266:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.rear
ALSA lib pcm.c:2266:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.center_lfe
ALSA lib pcm.c:2266:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.side
ALSA lib pcm_dmix.c:1029:(snd_pcm_dmix_open) unable to open slave
Cannot connect to server socket err = No such file or directory
Cannot connect to server request channel
jack server is not running or cannot be started
JackShmReadWritePtr::~JackShmReadWritePtr - Init not done for 4294967295, skipping unlock
JackShmReadWritePtr::~JackShmReadWritePtr - Init not done for 4294967295, skipping unlock
I want to suppress that output.
EDIT: testing a solution provided by @Matthias
#!/usr/bin/env python
import contextlib
import os
import subprocess
import sys
def main():
print("hello")
with silence():
print("there")
print("world")
with silence():
engage_command(command = "ls")
@contextlib.contextmanager
def silence():
devnull = os.open(os.devnull, os.O_WRONLY)
old_stderr = os.dup(2)
sys.stderr.flush()
os.dup2(devnull, 2)
os.close(devnull)
try:
yield
finally:
os.dup2(old_stderr, 2)
os.close(old_stderr)
def engage_command(
command = None
):
process = subprocess.Popen(
[command],
shell = True,
executable = "/bin/bash")
process.wait()
output, errors = process.communicate()
return output
if __name__ == "__main__":
main()
I have not been successful in suppressing the terminal output from the print
or the ls
and I'm not sure why.
For the ALSA errors in particular, you can use ALSA's snd_lib_error_set_handler
function as described for example in this question.
For one of my projects, I created a module called mute_alsa
, which looks like this:
import ctypes
ERROR_HANDLER_FUNC = ctypes.CFUNCTYPE(None, ctypes.c_char_p, ctypes.c_int,
ctypes.c_char_p, ctypes.c_int,
ctypes.c_char_p)
def py_error_handler(filename, line, function, err, fmt):
pass
c_error_handler = ERROR_HANDLER_FUNC(py_error_handler)
try:
asound = ctypes.cdll.LoadLibrary('libasound.so.2')
asound.snd_lib_error_set_handler(c_error_handler)
except OSError:
pass
And you use it simply by putting the following into your code:
import mute_alsa
In a more general sense, you can prevent anything from printing to
stderr
by simply closing the associated file descriptor. For
example, if we try to rm
a file that doesn't exist, we get an error
message printed to stderr
:
>>> import sys
>>> import os
>>> os.system('rm /doesnotexist')
rm: cannot remove ‘/doesnotexist’: Is a directory
256
If we close the stderr
file descriptor:
>>> os.close(sys.stderr.fileno())
We no longer see any error messages:
>>> os.system('rm /doesnotexist')
The downside to this approach is that it silences all messages to
stderr
, which means you will no longer see legitimate error
messages. This may lead to surprising failure modes (the program
exited without printing any errors!).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With