Python: как заглянуть в объект pty, чтобы избежать блокировки?
Я использую pty
для чтения не блокирующего stdout процесса, подобного этому:
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 ответа:
Да, используйте опрос модуля select:
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
Выбор .poll () Ответ очень аккуратный, но не работает на Windows. Альтернативой является следующее решение. Он не позволяет вам заглянуть в stdout, но предоставляет неблокирующую альтернативу 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()
Другие решения для неблокирующего чтения были предложены здесь , но не сработали для меня:
- решения, требующие чтения строки (в том числе основанные на очереди), всегда блокируются. Это сложно (невозможно?) чтобы убить поток, который выполняет readline. Он погибает только тогда, когда завершается процесс, который его создал, но не тогда, когда погибает процесс производства продукции.
- смешивание низкоуровневых fcntl с высокоуровневыми вызовами readline может не работать должным образом, как указывал anonnn.
- Использование select.poll() является аккуратным, но не работает на Windows в соответствии с python docs.
- Использование сторонних библиотек кажется излишним для этой задачи и добавляет дополнительные зависимости.