Обернуть подпроцесс 'stdout / stderr - PullRequest
7 голосов
/ 02 декабря 2010

Я бы хотел как захватить, так и отобразить вывод процесса, который я вызываю через подпроцесс Python.

Я думал, что мог бы просто передать свой файловый объект как именованные параметры stdout и stderr

Я вижу, что он обращается к атрибуту fileno, поэтому он что-то делает с объектом. Однако метод write() никогда не вызывается. Мой подход полностью отключен или я что-то упускаю?

class Process(object):
    class StreamWrapper(object):
        def __init__(self, stream):
            self._stream = stream
            self._buffer = []
        def _print(self, msg):
            print repr(self), msg
        def __getattr__(self, name):
            if not name in ['fileno']:
                self._print("# Redirecting: %s" % name)
            return getattr(self._stream, name)
        def write(self, data):
            print "###########"
            self._buffer.append(data)
            self._stream.write(data)
            self._stream.flush()
        def getBuffer(self):
            return self._buffer[:]
    def __init__(self, *args, **kwargs):
        print ">> Running `%s`" % " ".join(args[0])
        self._stdout = self.StreamWrapper(sys.stdout)
        self._stderr = self.StreamWrapper(sys.stderr)
        kwargs.setdefault('stdout', self._stdout)
        kwargs.setdefault('stderr', self._stderr)
        self._process = subprocess.Popen(*args, **kwargs)
        self._process.communicate()

Обновление:

Я также хотел бы поработать с управляющими символами ANSI для перемещения курсора и переопределения ранее выведенных данных. Я не знаю, является ли это правильным термином, но вот пример того, что я имел в виду: я пытаюсь автоматизировать некоторые вещи GIT, и там у них есть прогресс, который обновляется без записи в новую строку каждый раз. *

Обновление 2

Для меня важно, чтобы вывод подпроцесса отображался немедленно. Я пытался использовать subprocess.PIPE для захвата вывода и отображения его вручную, но я смог получить его только для отображения вывода, как только процесс завершился. Тем не менее, я хотел бы видеть вывод в режиме реального времени.

Ответы [ 4 ]

12 голосов
/ 02 декабря 2010

Stdin, stdout и stderr процесса должны быть настоящими файловыми дескрипторами.(Это на самом деле не ограничение, наложенное Python, а скорее как конвейеры работают на уровне ОС.) Поэтому вам понадобится другое решение.

Если вы хотите отслеживать оба stdout и stderr вв реальном времени вам потребуются асинхронный ввод-вывод или потоки.

  • асинхронный ввод-вывод: со стандартным синхронным (= блокирующим) вводом-выводом, чтениечтобы один из потоков мог заблокировать, запретив доступ к другому в режиме реального времени.Если вы работаете в Unix, вы можете использовать неблокирующий ввод / вывод, как описано в этот ответ .Однако на Windows вам не повезет с таким подходом.Подробнее об асинхронном вводе-выводе в Python и некоторых альтернативах показано в этом видео .

  • Threads: Другой распространенный способ решения этой проблемыпроблема заключается в создании одного потока для каждого дескриптора файла, из которого вы хотите читать в режиме реального времени.Потоки обрабатывают только дескриптор файла, к которому они относятся, поэтому блокировка ввода-вывода не повредит.

0 голосов
/ 02 декабря 2010

Файловый формат недостаточно близок. Это должен быть фактический файл с фактическим дескриптором файла. Используйте поддержку subprocess для каналов и считывайте их соответствующим образом.

0 голосов
/ 02 декабря 2010

Есть ли причина, по которой у вас есть класс внутри класса? И stdout и stderr могут взять любой файл, например строку, например, try. Поэтому достаточно просто передать открытый тип файла или stringIO, чтобы изменить поток

import sys
sys.stdout = open('test.txt','w')
print "Testing!"
sys.stdout.write('\nhehehe')
sys.stdout = sys.__stdout__
sys.exit(0)
0 голосов
/ 02 декабря 2010

Посмотрите здесь .

p = subprocess.Popen(cmd,
                 shell=True,
                 bufsize=64,
                 stdin=subprocess.PIPE,
                 stderr=subprocess.PIPE,
                 stdout=subprocess.PIPE)
...