Использование подпроцесса с select и pty зависает при захвате вывода

Я пытаюсь написать программу на Python, которая может взаимодействовать с другими программами. Это означает отправку данных stdin и получение данных stdout. Я не могу использовать pexpect ( хотя это определенно вдохновило некоторых дизайнеров).Процесс, который я сейчас использую, таков:

  1. Прикрепите pty к стандартному выводу подпроцесса
  2. Цикл, пока подпроцесс не завершится, проверяя subprocess.poll
    • Когда в стандартном выводе появятся данные, напишите, что данные немедленно в текущий стандартный вывод.
  3. Готово!

Я создал прототип кода (ниже), который работает, но, кажется, имеет один недостаток, который меня беспокоит. После завершения дочернего процесса родительский процесс зависает, если я не укажу тайм-аут при использовании select.select. Я бы предпочел не устанавливать тайм-аут. Это только кажется немного грязным. Однако все другие способы, которыми я пытался обойти проблему, похоже, не работают. Pexpect, кажется, обходит это, используя os.execv и pty.fork вместо subprocess. Popen и pty.openpty решение Я не предпочитаю. Я что-то не так делаю, когда проверяю жизнь подпроцесса? Мой подход неверен?

Код, который я использую, приведен ниже. Я использую это в Mac OS X 10.6.8, но мне нужно, чтобы оно работало и в Ubuntu 12.04.

Это исполнитель подпроцесса runner.py:

import subprocess
import select
import pty
import os
import sys

def main():
    master, slave = pty.openpty()

    process = subprocess.Popen(['python', 'outputter.py'], 
            stdin=subprocess.PIPE, 
            stdout=slave, stderr=slave, close_fds=True)

    while process.poll() is None:
        # Just FYI timeout is the last argument to select.select
        rlist, wlist, xlist = select.select([master], [], [])
        for f in rlist:
            output = os.read(f, 1000) # This is used because it doesn't block
            sys.stdout.write(output)
            sys.stdout.flush()
    print "**ALL COMPLETED**"

if __name__ == '__main__':
    main()

Это код подпроцесса outputter.py. Странные случайные части предназначены только для имитации программы, выводящей данные через случайные интервалы. Вы можете удалить его, если хотите. Это не имеет значения:

import time
import sys
import random

def main():
    lines = ['hello', 'there', 'what', 'are', 'you', 'doing']
    for line in lines:
        sys.stdout.write(line + random.choice(['', '\n']))
        sys.stdout.flush()
        time.sleep(random.choice([1,2,3,4,5])/20.0)
    sys.stdout.write("\ndone\n")
    sys.stdout.flush()

if __name__ == '__main__':
    main()

Спасибо за любую помощь, которую вы все можете оказать!

Дополнительное примечание

pty используется, потому что я хочу убедиться, что стандартный вывод не буферизуется.

6
задан ravenac95 23 June 2012 в 04:19
поделиться