在twisted的IRC clien周围写一个阻塞包装

2024-06-16 09:04:24 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图为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)。由于调用回调<IRCClient instance>.privmsg时需要返回对channel.__next__的调用,因此似乎没有一个干净的选项。在这里使用异常或线程似乎是错误的,有没有更简单的(阻塞?)使用twisted的方式会让这成为可能?在


Tags: inimportfornetircconnectgooglechannel
1条回答
网友
1楼 · 发布于 2024-06-16 09:04:24

一般来说,如果你试图以“阻塞”的方式使用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

^{pr2}$

请不要再注意addCallbacks。在一个修饰的生成器函数中,它被yield取代。如果您有一个使用不同API的Googler版本,这可能会更接近您所要求的内容(上面的一个应该与Twisted中的IRCClient一起工作——尽管我没有测试它)。对于Googler.join返回某种类型的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之上实现这个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)

要运行这个,您需要为run函数创建一个新的greenlet并切换到它,然后启动reactor。在

from greenlet import greenlet

def main():
    greenlet(run).switch()
    reactor.run()

run开始第一个异步操作时,它将切换回reactor greenlet(在本例中,这是“main”greenlet,但实际上并不重要)来完成异步操作。完成后,corotwine将回调转换为greenlet开关返回到run。因此run被赋予了直接运行的错觉,就像一个“正常”的同步程序。但请记住,这只是一种错觉。在

因此,可以尽量远离Twisted最常用的面向回调的样式。不过,这不一定是个好主意。在

相关问题 更多 >