Можно ли разделить/объединить выходные потоки subprocess.Popen?

Я пишу класс-обертку для использования с менеджером рабочих процессов. Я хочу логировать вывод из приложения (дочерний процесс, выполняемый через subprocess.Popen) определенным образом:

  • stdout дочернего процесса должен идти в лог файл и в stdout родительского,
  • stderr дочернего процесса должен идти в другой лог файл, но также в stdout родительского.

Т.е. весь вывод от дочернего процесса должен в итоге сливаться на stdout (как в subprocess.Popen(..., stderr=subprocess.STDOUT), так что я могу зарезервировать stderr для сообщений журнала от самой обертки. С другой стороны, дочерние потоки должны идти в разные файлы, чтобы обеспечить отдельную валидацию.

Я пробовал использовать вспомогательный класс "Tee", чтобы связать два потока (stdout и файл журнала) вместе, так что Tee.write пишет в оба потока. Однако это не может быть передано в Popen, потому что "подпроцесс" использует функции уровня ОС для записи (см. здесь: http://bugs.python.org/issue1631).

Проблема с моим текущим решением (фрагмент кода ниже, адаптированный в основном из здесь) заключается в том, что вывод на stdout может появляться не в правильном порядке.

Как я могу это преодолеть? Или я должен использовать совершенно другой подход? (Если я буду придерживаться приведенного ниже кода, как мне выбрать значение для количества байт в os.read?)

import subprocess, select, sys, os

call = ... # set this
process = subprocess.Popen(call, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
logs = {process.stdout: open("out.log", "w"), process.stderr: open("err.log", "w")}
done = {process.stdout: False, process.stderr: False}
while (process.poll() is None) or (not all(done.values())):
    ready = select.select([process.stdout, process.stderr], [], [])[0]
    for stream in ready:
        data = os.read(stream.fileno(), 1)
        if data:
            sys.stdout.write(data)
            logs[stream].write(data)
        else:
            done[stream] = True
logs[process.stdout].close()
logs[process.stderr].close()

Кстати, это решение с использованием "fcntl" у меня не сработало. И я пока не смог понять, как адаптировать это решение к моему случаю, поэтому не пробовал.

7
задан Community 23 May 2017 в 11:43
поделиться