python console executable pyaudio silent

¿Cómo se puede silenciar de manera general la salida del terminal de ejecutables ejecutados por funciones de Python?



console executable (2)

Quiero suprimir toda la salida del terminal producida por una función que ejecuta ejecutables.

Intenté suprimir la salida de una función de Python utilizando un administrador de contexto que redefine temporalmente stdout y stderr cada vez que se llama a la función. Esto suprime la salida del terminal producida por print llamadas de print en la función, pero no parece funcionar cuando la función llama a los ejecutables que producen la salida del terminal.

Entonces, ¿cómo podría suprimirse la salida de ejecutables llamados por las funciones de Python?

Mi codigo esta abajo. He incluido una función de ejemplo que llama a ls para intentar ilustrar el tipo de salida de terminal que quiero suprimir (aunque la función con la que estoy tratando es diferente).

#!/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()

En mi caso particular, estoy tratando de ejecutar la siguiente función (en lugar de la función ls anterior):

with propyte.silence(): stream = pyaudio.PyAudio().open( format = pyaudio.PyAudio().get_format_from_width(1), channels = 1, rate = bitrate, output = True )

Cuando se ejecuta, esto produce resultados como los siguientes:

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

Quiero suprimir esa salida.

EDITAR: probar una solución proporcionada por @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()

No he tenido éxito en suprimir la salida del terminal de la print o el ls y no estoy seguro de por qué.


Para los errores de ALSA en particular, puede usar la función snd_lib_error_set_handler de ALSA como se describe, por ejemplo, en esta pregunta .

Para uno de mis proyectos, creé un módulo llamado mute_alsa , que se ve así:

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

Y lo usa simplemente poniendo lo siguiente en su código:

import mute_alsa

En un sentido más general, puede evitar que cualquier cosa se imprima en stderr simplemente cerrando el descriptor de archivo asociado. Por ejemplo, si intentamos rm un archivo que no existe, se imprime un mensaje de error en stderr :

>>> import sys >>> import os >>> os.system(''rm /doesnotexist'') rm: cannot remove ‘/doesnotexist’: Is a directory 256

Si stderr el descriptor de archivo stderr :

>>> os.close(sys.stderr.fileno())

Ya no vemos ningún mensaje de error:

>>> os.system(''rm /doesnotexist'')

La desventaja de este enfoque es que silencia todos los mensajes para stderr , lo que significa que ya no verá mensajes de error legítimos. Esto puede llevar a modos de falla sorprendentes (¡el programa salió sin imprimir ningún error!).


Puede cambiar de PyAudio al módulo de dispositivo de sounddevice , que ya se encarga de silenciar la salida del terminal (ver #12 ). Así es como se hace allí (usando CFFI):

from cffi import FFI import os ffi = FFI() ffi.cdef(""" /* from stdio.h */ FILE* fopen(const char* path, const char* mode); int fclose(FILE* fp); FILE* stderr; /* GNU C library */ FILE* __stderrp; /* Mac OS X */ """) try: stdio = ffi.dlopen(None) devnull = stdio.fopen(os.devnull.encode(), b''w'') except OSError: return try: stdio.stderr = devnull except KeyError: try: stdio.__stderrp = devnull except KeyError: stdio.fclose(devnull)

Si desea una solución pura de Python, puede probar este administrador de contexto:

import contextlib import os import sys @contextlib.contextmanager def ignore_stderr(): 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)

Esta es una publicación de blog muy útil sobre el tema: http://eli.thegreenplace.net/2015/redirecting-all-kinds-of-stdout-in-python/ .

ACTUALIZAR:

El administrador de contexto anterior silencia la salida de error estándar ( stderr ), que se utiliza para los mensajes molestos de PortAudio mencionados en la pregunta original. Para deshacerse de la salida estándar ( stdout ), como en su pregunta actualizada, deberá reemplazar sys.stderr con sys.stdout y el descriptor de archivo 2 con el número 1 :

@contextlib.contextmanager def ignore_stdout(): devnull = os.open(os.devnull, os.O_WRONLY) old_stdout = os.dup(1) sys.stdout.flush() os.dup2(devnull, 1) os.close(devnull) try: yield finally: os.dup2(old_stdout, 1) os.close(old_stdout)