2012-06-21 3 views
18

У меня есть скрипт Python, который использует некоторые функции Python с закрытым ящиком (т. Е. Я не могу редактировать эти функции), предоставленные моим работодателем. Когда я вызываю эти функции, они выводят вывод на мой терминал linux, который я хотел бы подавить. Я попытался перенаправить stdout/stderr через;Подавить stdout/stderr print из функций Python

orig_out = sys.stdout 
sys.stdout = StringIO() 
rogue_function() 
sys.stdout = orig_out 

, но это не позволяет получить выход. Я думаю, что функции, которые я вызываю через Python (rogue_function() сверху), действительно являются обертками для скомпилированного C-кода, который фактически выполняет печать.

Кто-нибудь знает, как я могу сделать «глубокий захват» любой печати, переданной в stdout/stderr с помощью функции (и любых подфункций, которые выполняют вызовы)?

UPDATE:

я в конечном итоге принимает метод, описанный в выбранном ответ ниже и писать менеджер контекста подавить стандартный вывод и стандартный поток ошибок:

# Define a context manager to suppress stdout and stderr. 
class suppress_stdout_stderr(object): 
    ''' 
    A context manager for doing a "deep suppression" of stdout and stderr in 
    Python, i.e. will suppress all print, even if the print originates in a 
    compiled C/Fortran sub-function. 
     This will not suppress raised exceptions, since exceptions are printed 
    to stderr just before a script exits, and after the context manager has 
    exited (at least, I think that is why it lets exceptions through).  

    ''' 
    def __init__(self): 
     # Open a pair of null files 
     self.null_fds = [os.open(os.devnull,os.O_RDWR) for x in range(2)] 
     # Save the actual stdout (1) and stderr (2) file descriptors. 
     self.save_fds = [os.dup(1), os.dup(2)] 

    def __enter__(self): 
     # Assign the null pointers to stdout and stderr. 
     os.dup2(self.null_fds[0],1) 
     os.dup2(self.null_fds[1],2) 

    def __exit__(self, *_): 
     # Re-assign the real stdout/stderr back to (1) and (2) 
     os.dup2(self.save_fds[0],1) 
     os.dup2(self.save_fds[1],2) 
     # Close all file descriptors 
     for fd in self.null_fds + self.save_fds: 
      os.close(fd) 

Чтобы использовать это просто:

with suppress_stdout_stderr(): 
    rogue_function() 

Это работает «довольно хорошо». Он подавляет распечатку от функций изгоев, которые загромождали мой скрипт. Я заметил в тестировании, что он позволяет через поднятые исключения, а также некоторые логгер печати, и я не совсем понимаю, почему. Я думаю, что это имеет какое-то отношение к , когда эти сообщения отправляются в stdout/stderr (я думаю, что это происходит после выхода моего контекстного менеджера). Если кто-нибудь может подтвердить это, мне было бы интересно услышать подробности ...

+1

Does [Этот подход] (http://stackoverflow.com/a/978264/344821) (от связанная боковая панель)? – Dougal

+0

Вместо установки 'sys.stdout' в' StringIO() ', попробовали ли вы установить его в файл? то есть 'sys.stdout = open ('log.txt', 'w')' – carmenism

+0

Dougal, спасибо, это выглядит многообещающе, я попробую его завтра. nullpointer, я попробовал перенаправить его в класс NullPointer(), и это тоже не сработало. – jeremiahbuddha

ответ

5

This approach (найденный через связанную панель) может работать. Он переназначает дескрипторы файлов, а не только обертки для них в sys.stdout и т. Д.

-2

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

$> ./runscript.py > output.txt 
+0

Я не хочу подавлять весь вывод, сгенерированный скриптом, только побочный результат, генерируемый этими конкретными функциями. В противном случае, да, это было бы самым простым решением ... – jeremiahbuddha

+0

Помогло бы это: Добавить печать до и после этой конкретной функции. Разделите вывод с помощью регулярного выражения, чтобы избавиться от всего, что было между отпечатками, добавленными выше – GeneralBecos

1

Пробовали ли вы перенаправить stderr тоже? , например.

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

Также использование StringIO может использовать дополнительную память. Вместо этого вы можете использовать фиктивное устройство (например, http://coreygoldberg.blogspot.com/2009/05/python-redirect-or-turn-off-stdout-and.html).

+0

Да, я пробовал как stdout, так и stderr, без костей ... – jeremiahbuddha

+0

Возможно, код C записывает непосредственно в stdout; вы, вероятно, не сможете перенаправить это. Сожалею. – Bob

1

Мое решение похоже на ваше, но использует contextlib и немного короче и понятнее (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() 

Контекст почему я создал это в this blog post. Похоже на твое мнение.

Я использую его, как это в setup.py:

with stdchannel_redirected(sys.stderr, os.devnull): 
    if compiler.has_function('clock_gettime', libraries=['rt']): 
     libraries.append('rt') 
1

Не совсем по просьбе OP, но мне нужно, чтобы скрыть и хранить выход, и сделал, как следует:

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]) 

Использование:

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

(протестирован только с Pytho п 3)

EDIT: теперь позволяет выбрать stderr, stdout или оба, как в Hider([stdout, stderr])

+0

Это должен быть принятый ответ для python 3! можно также добавить seld._stderr == sys.stderr; sys.stderr = self <...> – neok

+0

@neok: немного обобщен, спасибо за предложение –