And*_*rov 3 python asynchronous twisted
我正在尝试为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)
Run Code Online (Sandbox Code Playgroud)
从他们的例子开始,我遇到了麻烦(代码有点冗长,所以我把它贴在这里).由于在调用channel.__next__回调时需要返回<IRCClient instance>.privmsg调用,因此似乎没有干净的选项.在这里使用异常或线程似乎是错误的,是否有更简单(阻塞?)的方式使用twisted会使这成为可能?
Jea*_*one 10
在一般情况下,如果你想在"堵"的方式来使用双绞线,你会碰到很多的困难,因为这既不它打算使用的方式,也不是大多数人的使用方式.
顺应流程通常会容易得多,在这种情况下,这意味着拥抱回调.你问题的回调式解决方案看起来像这样:
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()
Run Code Online (Sandbox Code Playgroud)
这不是绝对必要的(但我强烈建议你考虑尝试).一种选择是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()
Run Code Online (Sandbox Code Playgroud)
不要再注意了addCallbacks.它已被yield装饰的生成器功能所取代.如果你有一个Googler不同的API 版本(上面的那个应该与IRCClientTwisted 一起使用,因为它是写的 - 虽然我没有测试它),这可能会更接近你所要求的.完全有可能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,))
Run Code Online (Sandbox Code Playgroud)
这只是在已经存在的API之上实现此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,))
Run Code Online (Sandbox Code Playgroud)
实现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
Run Code Online (Sandbox Code Playgroud)
这又取决于一种新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)
Run Code Online (Sandbox Code Playgroud)
要运行它,您需要为该run函数创建一个新的greenlet 并切换到它,然后启动reactor.
from greenlet import greenlet
def main():
greenlet(run).switch()
reactor.run()
Run Code Online (Sandbox Code Playgroud)
当run进入第一个异步操作时,它会切换回reactor greenlet(在这种情况下是"主"greenlet,但这并不重要)让异步操作完成.完成后,corotwine将回调转换为greenlet切换回run.因此run被赋予直接运行的幻觉,就像"正常"的同步程序一样.但请记住,这只是一种幻觉.
因此,可以根据需要远离最常用于Twisted的回调导向样式.不过,这不一定是个好主意.
| 归档时间: |
|
| 查看次数: |
1219 次 |
| 最近记录: |