Неблокирующий метод для анализа (потокового) XML в Python

У меня есть XML-документ, поступающий через сокет, который мне нужно анализировать и реагировать на лету (то есть, анализировать частичное дерево). Мне бы хотелось, чтобы это был неблокирующий метод, чтобы я мог делать другие вещи, ожидая поступления дополнительных данных (без потоков).

Нечто подобное iterparse было бы идеальным, если бы оно заканчивало итерацию, когда буфер чтения был пуст, например:

context = iterparse(imaginary_socket_file_wrapper)
while 1:
    for event, elem in context:
        process_elem(elem)
    # iteration of context finishes when socket has no more data
    do_other_stuff()
    time.sleep(0.1)

Я думаю, SAX также был бы вариантом, но он кажется мне проще для моих нужд. Есть идеи?

Обновить:

Использование потоков - это нормально, но представляет уровень сложности, который я надеялся обойти. Я думал, что неблокирующие вызовы были бы хорошим способом сделать это, но я обнаружил, что это увеличивает сложность парсинга XML.

3 ответа

Решение

Погружение в истонченный источник предоставило мне решение. Вот простой пример построения дерева XML на лету и обработки элементов после их закрывающих тегов:

import xml.etree.ElementTree as etree

parser = etree.XMLTreeBuilder()

def end_tag_event(tag):
    node = self.parser._end(tag)
    print node

parser._parser.EndElementHandler = end_tag_event

def data_received(data):
    parser.feed(data)

В моем случае я получил данные из витой строки, но она должна работать и с неблокирующим сокетом.

Я думаю, что есть два компонента: неблокирующий сетевой ввод-вывод и потоково-ориентированный анализатор XML.

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

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

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

По общему признанию, Eventlet немного волшебен, но я считаю, что он имеет гораздо более легкую кривую обучения, чем Twisted, и приводит к более простому коду, потому что вам не нужно выворачивать свою логику "наизнанку", чтобы соответствовать структуре.

Если вы не будете использовать потоки, вы можете использовать цикл обработки событий и опрашивать неблокирующие сокеты.

asyncore стандартный модуль библиотеки для таких вещей. Twisted - это асинхронная библиотека для Python, но сложная и, вероятно, немного тяжелая для ваших нужд.

С другой стороны, multiprocessing является альтернативой нить нить потока, но я предполагаю, что вы не работаете 2.6.

Так или иначе, я думаю, вам придется использовать потоки, дополнительные процессы или создавать не менее сложную асинхронную магию.

Другие вопросы по тегам