2010-04-22 81 views
3

我试图写死简单的接口,连接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) 

their example工作,我遇到了麻烦(代码有点冗长,所以我粘贴它here)。由于呼叫channel.__next__需要回调时<IRCClient instance>.privmsg被称为退换,似乎没有成为一个清洁的选择。使用异常或线程似乎是错误的东西,有没有更简单的(阻塞?)使用扭曲的方式,这将使这成为可能?

回答

10

一般来说,如果你试图以“阻塞”的方式使用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最常用的回调导向风格。虽然这不一定是个好主意。

+0

甜,感谢广泛的职位!我似乎无法得到最后一个例子 - 获取“AssertionError:不要从反应堆greenlet运行gConnectTCP”。我做错什么了吗? – 2010-04-22 20:05:24

+0

我想我对第三个倒数第二个代码引用感到困惑:从哪里调用run()函数? – 2010-04-22 20:37:24

+1

我添加了另一个小代码片段和一些文字试图解释它。我希望这可以清除事情。 – 2010-04-24 03:18:45