Чтение из дескриптора открытого файла, который записывается в Python

Я знаю, что это классическая проблема в потоковой обработке, но я не знаю, как с ней справиться в Python. У меня есть дескриптор файла, который записывается активным процессом. Я хочу потреблять содержимое из этого файлового дескриптора построчно, но я не хочу зайти в тупик, ожидая чтения. Я буду читать до EOF или до 60 секунд циклического чтения, в зависимости от того, что наступит раньше. Совет о том, как это сделать, приветствуется. Описание моего псевдокода этой проблемы приведено ниже.

proc = genprocess("command")
found_a = False
found_b = False
start = time.time()
while True:
    line = proc.readline()
    while line:
        if not found_a and grep(pattern_a, line):
            found_a = True
            print "Found A, now looking for B"
        elif not found_b and grep(pattern_b, line):
            found_b = True
            print "Found B, all done"
            break
    if time.time() - start > 60:
        break
    else:
        time.sleep(5)

proc.kill()

Проблема в том, что это читает только одну строку на каждом интервале. Вместо этого я хочу, чтобы внутренняя часть цикла повторялась как можно больше раз, но не блокировала ожидание записи нового содержимого в файл. Как только он прочитает столько, сколько доступно, он должен заснуть на 5 секунд, чтобы позволить накопить больше контента.


person IanSR    schedule 29.03.2011    source источник


Ответы (2)


Если вы работаете в среде Unix, вы можете использовать модуль Python select для ожидания для данных об дескрипторе файла. Кроме того, вы можете использовать модуль fcntl Python для изменения дескриптора файла в неблокирующий режим. как описано в этом вопросе.

Например, предположим, что ваша переменная proc является обычным дескриптором файла, который поддерживает fileno():

file_num = proc.fileno()
old_flags = fcntl.fcntl(file_num, fcntl.F_GETFL)
fcntl.fcntl(file_num, fcntl.F_SETFL, old_flags | os.O_NONBLOCK)
person srgerg    schedule 29.03.2011
comment
Спасибо. Я на самом деле понял это сам после некоторых поисков. Я добавлю ответ на свой вопрос о том, как я это сделал. - person IanSR; 30.03.2011

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

    started = False
    while True:
        if (time.time() - start > wait_for) or started:
            break
        (rlist, wlist, xlist) = select([proc.stdout], [], [], wait_interval)
        if len(rlist) > 0:
            line = rlist[0].readline() # read one line (this blocks until '\n' is read)
        else: # nothing available to read from proc.stdout
            print ".",
            sys.stdout.flush()
            time.sleep(1)
            continue
        if re.search("daemon started", line):
            started = True

    if not started:
        proc.kill() # don't leave the process running if it didn't start properly

И если это то, что пользователь может использовать CTRL-C, то помещение всего этого в блок try/except и поиск KeyboardInterrupt позволяет вызывать proc.kill() вместо того, чтобы оставлять процесс работающим в фоновом режиме.

person IanSR    schedule 30.03.2011