围绕 Twisted 的 IRC 客户端编写阻塞包装器
我正在尝试为 IRC 库编写一个非常简单的界面,如下所示:
import simpleirc
connection = simpleirc.Connect('irc.freenode.net', 6667)
channel = connection.join('foo')
find_command = re.compile(r'google ([a-z]+)').findall
for msg in channel:
for t in find_command(msg):
channel.say("http://google.com/search?q=%s" % t)
从 他们的例子,我遇到了麻烦(代码有点长,所以我粘贴了它此处)。由于在调用回调
时需要返回对 channel.__next__
的调用,因此似乎没有一个干净的选项。使用异常或线程在这里似乎是错误的事情,是否有一种更简单(阻塞?)的使用扭曲的方法可以使这成为可能?
I'm trying to write a dead-simple interface for an IRC library, like so:
import simpleirc
connection = simpleirc.Connect('irc.freenode.net', 6667)
channel = connection.join('foo')
find_command = re.compile(r'google ([a-z]+)').findall
for msg in channel:
for t in find_command(msg):
channel.say("http://google.com/search?q=%s" % t)
Working from their example, I'm running into trouble (code is a bit lengthy, so I pasted it here). Since the call to channel.__next__
needs to be returned when the callback <IRCClient instance>.privmsg
is called, there doesn't seem to be a clean option. Using exceptions or threads seems like the wrong thing here, is there a simpler (blocking?) way of using twisted that would make this possible?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
一般来说,如果您尝试以“阻塞”方式使用 Twisted,您将会遇到很多困难,因为这既不是它的预期使用方式,也不是大多数人使用它的方式。
随波逐流通常要容易得多,在这种情况下,这意味着拥抱回调。您的问题的回调式解决方案看起来像这样:
这不是绝对必需的(但我强烈建议您考虑尝试它)。一种替代方案是
inlineCallbacks
:注意不再有
addCallbacks
。它已被修饰生成器函数中的yield
取代。如果您有一个具有不同 API 的Googler
版本,这可能会更接近您的要求(上面的版本应该与 Twisted 的IRCClient
一起使用,因为它是这样写的 -虽然我没有测试过)。Googler.join
完全有可能返回某种Channel
对象,并且该Channel
对象可以像这样进行迭代:只需在现有 API 的基础上实现此 API 即可。当然,
yield
表达式仍然存在,我不知道这会让您感到多么不安。 ;)可以进一步远离回调,并使异步操作工作所需的上下文切换完全不可见。这很糟糕,就像你家外面的人行道上散落着看不见的捕熊器一样。然而,这是可能的。使用像 corotwine 这样的东西,它本身基于 CPython 的第三方协程库,您可以实现 < code>Channel 自行执行上下文切换,而不是要求调用应用程序代码来执行此操作。结果可能类似于:
Channel
的实现可能类似于:这又取决于新的
Googler
方法getNextMessage
,这是基于现有IRCClient
回调的简单功能添加:要运行此功能,您需要为
run
函数创建一个新的 greenlet 并切换到它,然后启动反应器。当 run 到达第一个异步操作时,它会切换回反应器 greenlet(在本例中是“主”greenlet,但这并不重要)以完成异步操作。完成后,corotwine 将回调转为 greenlet 开关,返回到
run
。因此,run
被赋予了直接运行的错觉,就像“正常”同步程序一样。但请记住,这只是一种幻觉。因此,您可以根据需要远离 Twisted 最常使用的面向回调的样式。但这不一定是个好主意。
In general, if you're trying to use Twisted in a "blocking" way, you're going to run into a lot of difficulties, because that's neither the way it's intended to be used, nor the way in which most people use it.
Going with the flow is generally a lot easier, and in this case, that means embracing callbacks. The callback-style solution to your question would look something like this:
This isn't absolutely required (but I strongly suggest you consider trying it). One alternative is
inlineCallbacks
:Notice no more
addCallbacks
. It's been replaced byyield
in a decorated generator function. This could get even closer to what you asked for if you had a version ofGoogler
with a different API (the one above should work withIRCClient
from Twisted as it is written - though I didn't test it). It would be entirely possible forGoogler.join
to return aChannel
object of some sort, and for thatChannel
object to be iterable like this:It's only a matter of implementing this API on top of the ones already present. Of course, the
yield
expressions are still there, and I don't know how much this will upset you. ;)It's possible to go still further away from callbacks and make the context switches necessary for asynchronous operation to work completely invisible. This is bad for the same reason it would be bad for sidewalks outside your house to be littered with invisible bear traps. However, it's possible. Using something like corotwine, itself based on a third-party coroutine library for CPython, you can have the implementation of
Channel
do the context switching itself, rather than requiring the calling application code to do it. The result might look something like:with an implementation of
Channel
that might look something like:This in turn depends on a new
Googler
method,getNextMessage
, which is a straightforward feature addition based on existingIRCClient
callbacks:To run this, you create a new greenlet for the
run
function and switch to it, and then start the reactor.When
run
gets to its first asynchronous operation, it switches back to the reactor greenlet (which is the "main" greenlet in this case, but it doesn't really matter) to let the asynchronous operation complete. When it completes, corotwine turns the callback into a greenlet switch back intorun
. Sorun
is granted the illusion of running straight through, like a "normal" synchronous program. Keep in mind that it is just an illusion, though.So, it's possible to get as far away from the callback-oriented style that is most commonly used with Twisted as you want. It's not necessarily a good idea, though.