Python: как я могу использовать Twisted в качестве транспорта для SUDS?

У меня есть проект, основанный на Twisted, используемый для общаться с сетевыми устройствами, и я добавляю поддержку нового vendor (Citrix NetScaler), API которого является SOAP. К сожалению, поддержка SOAP в Twisted по-прежнему зависит от SOAPpy, что плохо даты. На самом деле с этого вопроса (я только что проверил), twisted.web.soap сам даже не обновлялся через 21 месяц!

Я хотел бы спросить, есть ли у кого-нибудь опыт, который они хотели бы делиться с использованием витой превосходной асинхронной транспортировки функциональность с SUDS. Кажется, что он подключается к пользовательскому Twisted транспорт будет естественным образом соответствовать SUDS 'Client.options.transport, я просто имею трудное время обертывание вокруг меня.

Я придумал способ вызова метода SOAP с помощью SUDS асинхронно, используя twisted.internet.threads.deferToThread(), но для меня это похоже на взлом.

Вот пример того, что я сделал, чтобы дать вам идею:

# netscaler is a module I wrote using suds to interface with NetScaler SOAP
# Source: http://bitbucket.org/jathanism/netscaler-api/src
import netscaler
import os
import sys
from twisted.internet import reactor, defer, threads

# netscaler.API is the class that sets up the suds.client.Client object
host = 'netscaler.local'
username = password = 'nsroot'
wsdl_url = 'file://' + os.path.join(os.getcwd(), 'NSUserAdmin.wsdl')
api = netscaler.API(host, username=username, password=password, wsdl_url=wsdl_url)

results = []
errors = []

def handleResult(result):
    print '\tgot result: %s' % (result,)
    results.append(result)

def handleError(err):
    sys.stderr.write('\tgot failure: %s' % (err,))
    errors.append(err)

# this converts the api.login() call to a Twisted thread.
# api.login() should return True and is is equivalent to:
# api.service.login(username=self.username, password=self.password)
deferred = threads.deferToThread(api.login)
deferred.addCallbacks(handleResult, handleError)

reactor.run()

Это работает так, как ожидалось, и отменяет возврат вызова api.login() до тех пор, пока он завершен, а не блокируется. Но, как я уже сказал, он не чувствует правильно.

Заранее благодарим за любую помощь, руководство, отзывы, критику, оскорбления или общие решения.

Обновление: Единственное решение, которое я нашел, это twisted-suds, который является вилкой Suds изменен для работы с Twisted.

Ответ 1

По умолчанию интерпретация транспорта в контексте Twisted, вероятно, является реализацией twisted.internet.interfaces.ITransport. На этом уровне вы в основном имеете дело с необработанными байтами, которые отправляются и принимаются по сокету определенного типа (UDP, TCP и SSL являются наиболее часто используемыми тремя). На самом деле это не то, что интересует библиотека интеграции SUDS/Twisted. Вместо этого вы хотите использовать HTTP-клиент, который SUDS может использовать для выполнения необходимых запросов и который представляет все данные ответа, чтобы SUDS мог определить, какой результат был. То есть, SUDS действительно не заботится о необработанных байтах в сети. Об этом говорят HTTP-запросы и ответы.

Если вы рассмотрите реализацию twisted.web.soap.Proxy (клиентская часть Twisted Web SOAP API), вы увидите, что на самом деле это не так. Это около 20 строк кода, которые склеивают SOAPpy до twisted.web.client.getPage. То есть, он подключает SOAPpy в Twisted так, как я описал выше.

В идеале SUDS предоставит какой-то API по строкам SOAPpy.buildSOAP и SOAPpy.parseSOAPRPC (возможно, API-интерфейсы будут немного сложнее или будут принимать еще несколько параметров - я не эксперт SOAP, поэтому Я не знаю, не хватает ли SOAPpy конкретных API-интерфейсов чего-то важного, но основная идея должна быть одинаковой). Тогда вы можете написать что-то вроде twisted.web.soap.Proxy на основе SUDS. Если twisted.web.client.getPage не обеспечивает достаточный контроль над запросами или достаточную информацию об ответах, вы также можете использовать twisted.web.client.Agent, который был недавно введен и предлагает гораздо больший контроль над всем процессом запроса/ответа. Но опять же, это действительно та же идея, что и текущий getPage -ограниченный код, просто более гибкая/выразительная реализация.

Только посмотрев документацию API для Client.options.transport, это похоже на то, что транспорт SUDS в основном является клиентом HTTP. Проблема с такой интеграцией заключается в том, что SUDS хочет отправить запрос, а затем сможет сразу получить ответ. Поскольку Twisted в основном основан на обратных вызовах, API-интерфейс HTTP-клиента Twisted не может сразу возвращать ответ на SUDS. Он может возвращать только Deferred (или эквивалент).

Вот почему все работает лучше, если отношения перевернуты. Вместо того, чтобы предоставлять SUDS клиент HTTP, с которым можно играть, предоставить SUDS и HTTP-клиенту третий кусок кода и позволить ему организовать взаимодействие.

Возможно, нет ничего невозможного в том, что все работает, создавая транспорт SUDS на основе Twisted (так называемый HTTP-клиент). Тот факт, что Twisted в первую очередь использует Deferred (aka callbacks) для раскрытия событий, не означает, что это единственный способ, которым это может работать. Используя стороннюю библиотеку, такую ​​как greenlet, можно предоставить API-интерфейс на основе сопрограмм, где запрос на асинхронную операцию включает в себя переход от одной сопрограммы к другой, а события доставляются путем переключения обратно в исходную сопрограмму, Существует проект под названием corotwine, который может сделать именно это. Возможно, это будет возможно, чтобы предоставить SUDS в виде HTTP-клиентского API, который он хочет; однако, это не гарантировано. Это зависит от того, что SUDS не прерывается, когда внезапно вставлен контекстный переключатель, где раньше не было ни одного. Это очень тонкое и хрупкое свойство SUDS и может быть легко изменено (непреднамеренно, даже) разработчиками SUDS в будущей версии, поэтому, вероятно, это не идеальное решение, даже если вы можете заставить его работать сейчас (если только вы не можете получить сотрудничество от сопровождающих SUDS в виде обещания проверить свой код в такой конфигурации, чтобы он продолжал работать).

В стороне, причина, по которой поддержка Twisted Web SOAP по-прежнему основана на SOAPpy и не была изменена почти на два года, заключается в том, что некоторая явная замена SOAPpy никогда не появлялась. Было много претендентов (Какие существуют клиентские библиотеки SOAP для Python, а где - документация для них? охватывает несколько из них). Если ситуация когда-либо оседает, может возникнуть смысл попытаться обновить встроенную поддержку SOAP. До тех пор я думаю, что имеет смысл делать эти интеграционные библиотеки по отдельности, поэтому их можно обновлять более легко, поэтому сам Twisted не заканчивается большой кучей различных SOAP-интеграции, которые никто не хочет (что было бы хуже, чем текущая ситуация, когда есть только один модуль интеграции SOAP, который никто не хочет).