Python:如何使用 Twisted 作为 SUDS 的传输?
我有一个基于 Twisted 的项目 与网络设备通信,我正在添加对新的支持 API 为 SOAP 的供应商 (Citrix NetScaler)。不幸的是 Twisted 中对 SOAP 的支持仍然依赖于 SOAPpy,但它已经严重过时了 的日期。事实上,截至这个问题(我刚刚检查), twisted .web.soap
其本身已经 21 个月没有更新了!
我想问一下有没有人有经验愿意 分享利用 Twisted 出色的异步传输 SUDS 的功能。看起来就像插入一个定制的 Twisted Transport 非常适合 SUDS 的 Client.options.transport
,我只是 我很难理解它。
我确实想出了一种用 SUDS 调用 SOAP 方法的方法 通过利用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 一起使用。
I have a project that is based on Twisted used to
communicate with network devices and I am adding support for a new
vendor (Citrix NetScaler) whose API is SOAP. Unfortunately the
support for SOAP in Twisted still relies on SOAPpy
, which is badly out
of date. In fact as of this question (I just checked), twisted.web.soap
itself hasn't even been updated in 21 months!
I would like to ask if anyone has any experience they would be willing
to share with utilizing Twisted's superb asynchronous transport
functionality with SUDS. It seems like plugging in a custom Twisted
transport would be a natural fit in SUDS' Client.options.transport
, I'm just having
a hard time wrapping my head around it.
I did come up with a way to call the SOAP method with SUDS
asynchronously by utilizing twisted.internet.threads.deferToThread()
,
but this feels like a hack to me.
Here is an example of what I've done, to give you an idea:
# 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()
This works as expected and defers return of the api.login()
call until
it is complete, instead of blocking. But as I said, it doesn't feel
right.
Thanks in advance for any help, guidance, feedback, criticism,
insults, or total solutions.
Update: The only solution I've found is twisted-suds, which is a fork of Suds modified to work with Twisted.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
Twisted 上下文中 transport 的默认解释可能是
twisted.internet.interfaces.ITransport
的实现。在这一层,您基本上处理通过某种套接字发送和接收的原始字节(UDP、TCP 和 SSL 是最常用的三种)。这并不是 SUDS/Twisted 集成库真正感兴趣的。相反,您想要的是一个 HTTP 客户端,SUDS 可以使用它来发出必要的请求,并显示所有响应数据,以便 SUDS 可以确定结果曾是。也就是说,SUDS 并不真正关心网络上的原始字节。它关心的是HTTP请求和响应。如果您检查
twisted.web.soap.Proxy
(Twisted Web SOAP API 的客户端部分)的实现,您会发现它实际上并没有做太多事情。将SOAPpy
粘合到twisted.web.client.getPage
大约有 20 行代码。也就是说,它按照我上面描述的方式将 SOAPpy 连接到 Twisted。理想情况下,SUDS 将提供某种类似于 SOAPpy.buildSOAP 和 SOAPpy.parseSOAPRPC 的 API(也许 API 会更复杂一些,或者接受更多)参数 - 我不是 SOAP 专家,所以我不知道 SOAPpy 的特定 API 是否缺少一些重要的东西 - 但基本思想应该是相同的)。然后您可以编写基于 SUDS 的类似
twisted.web.soap.Proxy
的内容。如果twisted.web.client.getPage
无法提供对请求的足够控制或有关响应的足够信息,您也可以使用twisted.web.client.Agent
,这是最近推出的,可以对整个请求/响应过程提供更多控制。但同样,这实际上与当前基于getPage
的代码的想法相同,只是更灵活/更具表现力的实现。刚刚查看了
Client.options.transport
的 API 文档,听起来 SUDS 传输基本上是一个 HTTP 客户端。这种集成的问题在于 SUDS 希望发送请求,然后能够立即获得响应。由于 Twisted 主要基于回调,因此基于 Twisted 的 HTTP 客户端 API 无法立即向 SUDS 返回响应。它只能返回一个Deferred
(或等效值)。这就是为什么如果关系颠倒的话事情会更好。不要为 SUDS 提供一个 HTTP 客户端来使用,而是为第三段代码提供 SUDS 和 HTTP 客户端,并让它编排交互。
不过,通过创建基于 Twisted 的 SUDS 传输(也称为 HTTP 客户端)来使事情正常运行可能并非不可能。 Twisted 主要使用 Deferred(又名回调)来公开事件这一事实并不意味着这是它唯一的工作方式。通过使用 Greenlet 等第三方库,可以提供基于协程的 API,其中异步操作的请求涉及将执行从一个协程切换到另一个协程,并通过切换来传递事件回到原来的协程。有一个名为 corotwine 的项目可以做到这一点。 也许可以使用它来为 SUDS 提供它想要的 HTTP 客户端 API;但是,这并不能保证。这取决于当上下文切换突然插入到之前没有上下文切换的地方时,SUDS 不会中断。这是 SUDS 的一个非常微妙和脆弱的属性,SUDS 开发人员可以在未来的版本中轻松更改(甚至是无意的),因此它可能不是理想的解决方案,即使您可以获得它现在就可以工作(除非您可以从 SUDS 维护者那里获得合作,并承诺在这种配置中测试他们的代码以确保其继续工作)。
顺便说一句,Twisted Web 的 SOAP 支持仍然基于 SOAPpy 并且近两年没有修改的原因是,还没有出现 SOAPpy 的明确替代品。有很多竞争者(Python 存在哪些 SOAP 客户端库,它们的文档在哪里? 涵盖了其中的几个)。如果事情稳定下来,尝试更新 Twisted 的内置 SOAP 支持可能是有意义的。在那之前,我认为单独进行这些集成库更有意义,这样它们就可以更容易地更新,因此 Twisted 本身不会最终得到一大堆没人想要的不同的 SOAP 集成(这会 比目前的情况更糟糕,目前只有一个没人想要的 SOAP 集成模块)。
The default interpretation of transport in the context of Twisted is probably an implementation of
twisted.internet.interfaces.ITransport
. At this layer, you're basically dealing with raw bytes being sent and received over a socket of some sort (UDP, TCP, and SSL being the most commonly used three). This isn't really what a SUDS/Twisted integration library is interested in. Instead, what you want is an HTTP client which SUDS can use to make the necessary requests and which presents all of the response data so that SUDS can determine what the result was. That is to say, SUDS doesn't really care about the raw bytes on the network. What it cares about is the HTTP requests and responses.If you examine the implementation of
twisted.web.soap.Proxy
(the client part of the Twisted Web SOAP API), you'll see that it doesn't really do much. It's about 20 lines of code that gluesSOAPpy
totwisted.web.client.getPage
. That is, it's hooking SOAPpy in to Twisted in just the way I described above.Ideally, SUDS would provide some kind of API along the lines of
SOAPpy.buildSOAP
andSOAPpy.parseSOAPRPC
(perhaps the APIs would be a bit more complicated, or accept a few more parameters - I'm not a SOAP expert, so I don't know if SOAPpy's particular APIs are missing something important - but the basic idea should be the same). Then you could write something liketwisted.web.soap.Proxy
based on SUDS instead. Iftwisted.web.client.getPage
doesn't offer enough control over the requests or enough information about the responses, you could also usetwisted.web.client.Agent
instead, which is more recently introduced and offers much more control over the whole request/response process. But again, that's really the same idea as the currentgetPage
-based code, just a more flexible/expressive implementation.Having just looked at the API documentation for
Client.options.transport
, it sounds like a SUDS transport is basically an HTTP client. The problem with this kind of integration is that SUDS wants to send a request and then be able to immediately get the response. Since Twisted is largely based on callbacks, a Twisted-based HTTP client API can't immediately return a response to SUDS. It can only return aDeferred
(or equivalent).This is why things work better if the relationship is inverted. Instead of giving SUDS an HTTP client to play with, give SUDS and an HTTP client to a third piece of code and let it orchestrate the interactions.
It may not be impossible to have things work by creating a Twisted-based SUDS transport (aka HTTP client), though. The fact that Twisted primarily uses
Deferred
(aka callbacks) to expose events doesn't mean that this is the only way it can work. By using a third-party library such asgreenlet
, it's possible to provide a coroutine-based API, where a request for an asynchronous operation involves switching execution from one coroutine to another, and events are delivered by switching back to the original coroutine. There is a project called corotwine which can do just this. It may be possible to use this to provide SUDS with the kind of HTTP client API it wants; however, it's not guaranteed. It depends on SUDS not breaking when a context switch is suddenly inserted where previously there was none. This is a very subtle and fragile property of SUDS and can easily be changed (unintentionally, even) by the SUDS developers in a future release, so it's probably not the ideal solution, even if you can get it to work now (unless you can get cooperation from the SUDS maintainers in the form of a promise to test their code in this kind of configuration to ensure it continues to work).As an aside, the reason Twisted Web's SOAP support is still based on SOAPpy and hasn't been modified for nearly two years is that no clear replacement for SOAPpy has ever shown up. There have been many contenders (What SOAP client libraries exist for Python, and where is the documentation for them? covers several of them). If things ever settle down, it may make sense to try to update Twisted's built-in SOAP support. Until then, I think it makes more sense to do these integration libraries separately, so they can be updated more easily and so Twisted itself doesn't end up with a big pile of different SOAP integration that no one wants (which would be worse than the current situation, where there's just one SOAP integration module that no one wants).