Python: Как заглянуть в объект pty, чтобы избежать блокировки? - PullRequest
5 голосов
/ 21 июня 2011

Я использую pty для чтения, не блокируя стандартный вывод процесса, подобного этому:

import os
import pty
import subprocess

master, slave = pty.openpty()

p = subprocess.Popen(cmd, stdout = slave)

stdout = os.fdopen(master)
while True:
    if p.poll() != None:
        break

    print stdout.readline() 

stdout.close()

Все работает нормально, за исключением того, что while-loop иногда блокирует.Это связано с тем, что строка print stdout.readline() ожидает, что что-то будет прочитано из stdout.Но если программа уже прервалась, мой маленький скрипт там зависнет навсегда.

Мой вопрос: есть ли способ заглянуть в объект stdout и проверить, есть ли данные, доступные для чтения?Если это не тот случай, он должен продолжить через while-loop, где он обнаружит, что процесс фактически уже завершен, и прервет цикл.

Ответы [ 2 ]

10 голосов
/ 27 июня 2011

Да, используйте опрос select module :

import select
q = select.poll()
q.register(stdout,select.POLLIN)

и во время использования:

l = q.poll(0)
if not l:
    pass # no input
else:
    pass # there is some input
2 голосов
/ 15 марта 2013

Ответ select.poll () очень аккуратный, но не работает в Windows.Следующее решение является альтернативой.Он не позволяет просматривать стандартный вывод, но предоставляет неблокирующую альтернативу readline () и основан на этом ответе :

from subprocess import Popen, PIPE
from threading import Thread
def process_output(myprocess): #output-consuming thread
    nextline = None
    buf = ''
    while True:
        #--- extract line using read(1)
        out = myprocess.stdout.read(1)
        if out == '' and myprocess.poll() != None: break
        if out != '':
            buf += out
            if out == '\n':
                nextline = buf
                buf = ''
        if not nextline: continue
        line = nextline
        nextline = None

        #--- do whatever you want with line here
        print 'Line is:', line
    myprocess.stdout.close()

myprocess = Popen('myprogram.exe', stdout=PIPE) #output-producing process
p1 = Thread(target=process_output, args=(dcmpid,)) #output-consuming thread
p1.daemon = True
p1.start()

#--- do whatever here and then kill process and thread if needed
if myprocess.poll() == None: #kill process; will automatically stop thread
    myprocess.kill()
    myprocess.wait()
if p1 and p1.is_alive(): #wait for thread to finish
    p1.join()

Другие решения для неблокирующего чтениябыло предложено здесь , но у меня не получилось:

  1. Решения, которые требуют readline (включая основанные на очереди), всегда блокируются.Трудно (невозможно?) Убить поток, который выполняет readline.Он будет уничтожен только после завершения процесса, который его создал, но не когда процесс создания вывода будет уничтожен.
  2. Смешивание низкоуровневого fcntl с высокоуровневыми вызовами readline может работать некорректно, как указывал anonnn.
  3. Использование select.poll () изящно, но не работает в Windows в соответствии с документами Python.
  4. Использование сторонних библиотек кажется излишним для этой задачи и добавляет дополнительные зависимости.
...