2016-04-30 9 views
1

Я хочу подавить весь вывод терминала, созданный функцией, которая запускает исполняемые файлы.Как можно отключить вывод терминалов, выполняемых функциями Python, в общем виде?

Я попытался подавить вывод функции Python с помощью диспетчера контекста, который временно переопределяет stdout и stderr каждый раз, когда вызывается функция. Это подавляет вывод терминала, вызванный print вызовами в функции, но, похоже, не работает, когда функция вызывает исполняемые файлы, которые производят вывод терминала.

Итак, как можно скрыть вывод исполняемых файлов, вызываемых функциями Python?

Мой код ниже. Я включил пример функции, которая вызывает ls, чтобы попытаться проиллюстрировать тип вывода терминала, который я хочу подавить (хотя функция, с которой я имею дело, отличается).

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

В моем конкретном случае, я пытаюсь запустить следующую функцию (вместо ls функции выше):

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

При запуске, это производит вывод наподобие следующего:

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 

Я хочу подавить этот выход.


EDIT: тестирование a solution предоставленного @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() 

Я не был успешным в подавлении терминального выхода из print или ls, и я не знаю, почему.

ответ

1

Вы можете переключиться с PyAudio на модуль sounddevice, который уже заботится о том, чтобы отключить вывод терминала (см. #12). Вот как это делается там (с использованием 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) 

Если вы хотите чистое решение Python, вы можете попробовать этот контекст менеджер:

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) 

Это очень полезный сообщение в блоге о тема: http://eli.thegreenplace.net/2015/redirecting-all-kinds-of-stdout-in-python/.


ОБНОВЛЕНИЕ:

Менеджер контекста выше отключает стандартный вывод ошибок (stderr), который используется для раздражающих сообщений от Portaudio, упомянутых в первоначальном вопросе. Для того, чтобы избавиться от стандартного вывода (stdout), как и в обновленной вопрос, вы должны будете заменить sys.stderr с sys.stdout и дескриптор файла 2 с номером 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) 
+0

Большое спасибо за подробное решение. Я буду исследовать модуль sounddevice, как было предложено, и рассмотрит его решение для отключения PortAudio. Для чистого общего подхода к отключению терминала полностью (будь то для вывода Python или исполняемых файлов, запущенных Python), которые вы предложили, мне не удалось заставить его подавить вывод терминала. Я поместил свой код в редактирование вопроса. Есть ли у вас какое-либо представление о том, как изменить его так, чтобы вывод терминала 'print' и' ls' был подавлен? – d3pd

+0

А, вот и все. Нужно было обрабатывать stdout. Еще раз спасибо за четкое и подробное решение. – d3pd

2

Для ошибок ALSA, в частности, вы можете использовать функцию ALSA snd_lib_error_set_handler, как описано, например, in this question.

Для одного из моих проектов, я создал модуль, называемый mute_alsa, который выглядит следующим образом:

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 

И вы используете его, просто поместив следующий код в ваш код:

import mute_alsa 

В более общий смысл, вы можете предотвратить что-нибудь от печати до stderr, просто закрыв связанный файловый дескриптор.Для Например, если попытаться rm файл, который не существует, мы получаем ошибку сообщение об распечатаны на stderr:

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

Если закрыть stderr дескриптор файла:

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

Мы больше не отображаются сообщения об ошибках:

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

Недостатком этого подхода является зарегистрированным пользователем сообщений для: stderr, что означает, что вы больше не сможете увидеть правильную ошибку. сообщений. Это может привести к неожиданным отказам (программа вышла без печати каких-либо ошибок!).

+0

Эй, спасибо большое за ваш подробное решение (и для предвыборного предложения ALSA без звука). Когда я помещаю 'os.close (sys.stdout.fileno())' и 'os.close (sys.stderr.fileno())' в функции ввода или init диспетчера контекста выше, вывод терминала не похоже, подавляются. Вы видите, что мне не хватает? – d3pd

+0

Это замечательно, спасибо! – Eugene

 Смежные вопросы

  • Нет связанных вопросов^_^