一般来说,如果你试图以“阻塞”的方式使用Twisted,你会碰到很多困难,因为这既不是它打算使用的方式,也不是它的方式大多数人使用它。
往低处流通常是轻松了很多,在这种情况下,这意味着欣然接受回调。回调式的解决方案,您的问题将是这个样子:
import re
from twisted.internet import reactor, protocol
from twisted.words.protocols import irc
find_command = re.compile(r'google ([a-z]+)').findall
class Googler(irc.IRCClient):
def privmsg(self, user, channel, message):
for text in find_command(message):
self.say(channel, "http://google.com/search?q=%s" % (text,))
def connect():
cc = protocol.ClientCreator(reactor, Googler)
return cc.connectTCP(host, port)
def run(proto):
proto.join(channel)
def main():
d = connect()
d.addCallback(run)
reactor.run()
这不是绝对必需的(但我强烈建议你考虑尝试它)。一种选择是inlineCallbacks
:
import re
from twisted.internet import reactor, protocol, defer
from twisted.words.protocols import irc
find_command = re.compile(r'google ([a-z]+)').findall
class Googler(irc.IRCClient):
def privmsg(self, user, channel, message):
for text in find_command(message):
self.say(channel, "http://google.com/search?q=%s" % (text,))
@defer.inlineCallbacks
def run():
cc = protocol.ClientCreator(reactor, Googler)
proto = yield cc.connectTCP(host, port)
proto.join(channel)
def main():
run()
reactor.run()
通知没有更多addCallbacks
。装饰发电机功能已被yield
取代。这有可能会更接近你问什么,如果你有一个版本的Googler
用不同的API(上面应该有IRCClient
从扭曲的工作,因为它是写 - 虽然我没有测试)。这将是完全可能的Googler.join
回到某种形式的Channel
对象,并为Channel
对象可迭代这样的:
@defer.inlineCallbacks
def run():
cc = protocol.ClientCreator(reactor, Googler)
proto = yield cc.connectTCP(host, port)
channel = proto.join(channel)
for msg in channel:
msg = yield msg
for text in find_command(msg):
channel.say("http://google.com/search?q=%s" % (text,))
这只是对那些已经存在的基础上实现这个API的问题。当然,yield
表情仍然存在,我不知道有多少,这将让你心烦意乱。 ;)
它可能去仍回调渐行渐远,使上下文切换必需的异步操作的工作完全不可见。这同样也是不好的,因为你的房子外面的人行道会被看不见的熊陷阱所困扰。但是,这是可能的。使用类似corotwine,本身基于第三方的协同程序库CPython中,你可以有Channel
实施做上下文切换本身,而不需要调用应用程序代码来做到这一点。结果可能看起来像:
from corotwine import protocol
def run():
proto = Googler()
transport = protocol.gConnectTCP(host, port)
proto.makeConnection(transport)
channel = proto.join(channel)
for msg in channel:
for text in find_command(msg):
channel.say("http://google.com/search?q=%s" % (text,))
与Channel
的实现,可能看起来像:
from corotwine import defer
class Channel(object):
def __init__(self, ircClient, name):
self.ircClient = ircClient
self.name = name
def __iter__(self):
while True:
d = self.ircClient.getNextMessage(self.name)
message = defer.blockOn(d)
yield message
这又取决于新Googler
方法,getNextMessage
,这是一个简单的功能,除了基于现有IRCClient
回调:
from twisted.internet import defer
class Googler(irc.IRCClient):
def connectionMade(self):
irc.IRCClient.connectionMade(self)
self._nextMessages = {}
def getNextMessage(self, channel):
if channel not in self._nextMessages:
self._nextMessages[channel] = defer.DeferredQueue()
return self._nextMessages[channel].get()
def privmsg(self, user, channel, message):
if channel not in self._nextMessages:
self._nextMessages[channel] = defer.DeferredQueue()
self._nextMessages[channel].put(message)
要运行这个,你创建一个新的greenlet˚F或run
功能并切换到它,然后启动反应器。
from greenlet import greenlet
def main():
greenlet(run).switch()
reactor.run()
当run
到达其第一异步操作,它切换回反应器greenlet(在这种情况下,“主” greenlet,但它其实并不重要),以便让异步操作完成。完成后,corotwine将回拨变为greenlet切换回run
。所以run
被授予直通跑动的幻觉,就像一个“正常”的同步程序。请记住,这只是一种幻觉。
因此,可以尽可能远离Twisted最常用的回调导向风格。虽然这不一定是个好主意。
甜,感谢广泛的职位!我似乎无法得到最后一个例子 - 获取“AssertionError:不要从反应堆greenlet运行gConnectTCP”。我做错什么了吗? – 2010-04-22 20:05:24
我想我对第三个倒数第二个代码引用感到困惑:从哪里调用run()函数? – 2010-04-22 20:37:24
我添加了另一个小代码片段和一些文字试图解释它。我希望这可以清除事情。 – 2010-04-24 03:18:45