Захват stdout в рамках одного процесса в Python

У меня есть python script, который вызывает кучу функций, каждый из которых записывает вывод в stdout. Иногда, когда я запускаю его, я хотел бы отправить результат в электронном письме (вместе с сгенерированным файлом). Я хотел бы знать, как я могу захватить вывод в памяти, поэтому я могу использовать модуль email для создания электронной почты.

Мои идеи до сих пор были:

  • используйте файл с отображением памяти (но мне кажется, что мне нужно зарезервировать место на диске для этого, и я не знаю, сколько будет времени на выходе)
  • обойти все это и передать вывод на sendmail (но это может быть сложно, если я также хочу прикрепить файл)

Ответ 1

Вы сказали, что ваш script "вызывает кучу функций", поэтому я предполагаю, что они являются функциями python, доступными из вашей программы. Я также предполагаю, что вы используете print для генерации вывода во всех этих функциях. Если это так, вы можете просто заменить sys.stdout на StringIO.StringIO, который перехватит все, что вы пишете. Затем вы можете, наконец, вызвать метод .getValue на своем StringIO, чтобы получить все, что было отправлено на выходной канал. Это также будет работать для внешних программ, используя модуль подпроцесса, который записывается в sys.stdout.

Это дешевый способ. Я бы рекомендовал, чтобы вы делали свой вывод с помощью модуля logging. У вас будет гораздо больше контроля над тем, как он выводится, и вы также можете легко управлять им.

Ответ 2

Я изменил "Нет ответа", чтобы сделать его менеджером контекста:

import sys, StringIO, contextlib

class Data(object):
    pass

@contextlib.contextmanager
def capture_stdout():
    old = sys.stdout
    capturer = StringIO.StringIO()
    sys.stdout = capturer
    data = Data()
    yield data
    sys.stdout = old
    data.result = capturer.getvalue()

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

with capture_stdout() as capture:
    print 'Hello'
    print 'Goodbye'
assert capture.result == 'Hello\nGoodbye\n'

Ответ 3

Это довольно просто для захвата вывода.

import sys, StringIO
old_stdout = sys.stdout
capturer = StringIO.StringIO()
sys.stdout = capturer
#call functions
print "Hi"
#end functions
sys.stdout = old_stdout
output = capturer.getvalue()

Ответ 4

И я модифицировал Gary Robinson, чтобы убедиться, что stdout всегда восстанавливается, даже если есть исключение:

import sys, StringIO, contextlib

class Data(object):
    pass

@contextlib.contextmanager
def capture_stdout():
    old = sys.stdout
    capturer = StringIO.StringIO()
    data = Data()
    try:
        sys.stdout = capturer
        yield data
    finally:
        sys.stdout = old
        data.result = capturer.getvalue()