twisted 诗歌客户端代码分析:嵌套着的多层deferred。

http://krondo.com/?p=2159

http://blog.sina.com.cn/s/blog_704b6af70100qay3.html

花了很长的时间才看懂了这段代码的运行过程。该代码是上面两个网页中的twisted 教程中的第13部分的内容,旨在使用嵌套式的deferred建立一个诗歌下载客户端。

这篇文章的主要难点在于对多层deferred的callback函数的调用顺序的理解。


1.服务器提供诗歌的下载和诗歌的转换,两个功能。

2.该客户端首先从服务器上面下载诗歌,之后再使用服务器,对诗歌进行转换。

3.建立两个protocol,两个Factory.

4.PoetryProtocol与PoetryClientFactory用于下载诗歌。

5.TransformClientProtocol与TransformClientFactory用于对诗歌进行转换。

6.在主函数main中进行了三次添加callback。分别为:

d.addCallback(try_to_cummingsify)

d.addCallbacks(got_poem,poem_failed)

d.addBoth(poem_done)

7.在get_poetry(host,port)中为外部的deferred生成处。该函数建立TCP链接,从服务器端下载诗歌。

如果下载成功则在poem_finished中调用callback。则try_to_cummingsify开始执行。

否则,在clientConnectionFailed中调用errback。则首先pass_through然后直接执行poem_failed,再执行peom_done结束。

8.在try_to_cummingsify中建立了一个内部deferred,并且建立TCP链接,传输当前poem到服务器,然后接受转换好了的poem。并且向内部的deferred添加了一个callback

d.addErrback(fail)

9.如果cummingsify成功,则调用handlePoem,在handlePoem调用callback。由于内部deferred中之添加了fail作为errback。因此这个callback是直接pass_through的。

10.如果cummingsify失败,则调用clientConnectionLost,在其中调用d.errback,此时实际上执行了fail函数。

11.这时候外层的try_to_cummingsify函数执行完毕。开始执行got_poem。如果cummingsify成功,这时的poem为cum后的poem。否则为原poem,因为fail函数返回原poem。

12.最后执行poem_done,结束。


源代码如下:

# This is the Twisted Get Poetry Now! client, version 6.0

import optparse, sys

from twisted.internet import defer
from twisted.internet.protocol import Protocol, ClientFactory
from twisted.protocols.basic import NetstringReceiver


def parse_args():
    usage = """usage: %prog [options] [hostname]:port ...

This is the Get Poetry Now! client, Twisted version 6.0
Run it like this:

python get-poetry.py xform-port port1 port2 ...

If you are in the base directory of the twisted-intro package,
you could run it like this:

python twisted-client-6/get-poetry.py 10001 10002 10003

to grab poetry from servers on ports 10002, and 10003 and transform
it using the server on port 10001.

Of course, there need to be appropriate servers listening on those
ports for that to work.
"""

    parser = optparse.OptionParser(usage)

    _, addresses = parser.parse_args()

    if len(addresses) < 2:
        print parser.format_help()
        parser.exit()

    def parse_address(addr):
        if ':' not in addr:
            host = '127.0.0.1'
            port = addr
        else:
            host, port = addr.split(':', 1)

        if not port.isdigit():
            parser.error('Ports must be integers.')

        return host, int(port)

    return map(parse_address, addresses)


class PoetryProtocol(Protocol):

    poem = ''

    def dataReceived(self, data):
        self.poem += data

    def connectionLost(self, reason):
        self.poemReceived(self.poem)

    def poemReceived(self, poem):
        self.factory.poem_finished(poem)


class PoetryClientFactory(ClientFactory):

    protocol = PoetryProtocol

    def __init__(self, deferred):
        self.deferred = deferred

    def poem_finished(self, poem):
        if self.deferred is not None:
            d, self.deferred = self.deferred, None
            d.callback(poem)

    def clientConnectionFailed(self, connector, reason):
        if self.deferred is not None:
            d, self.deferred = self.deferred, None
            d.errback(reason)


class TransformClientProtocol(NetstringReceiver):

    def connectionMade(self):
        self.sendRequest(self.factory.xform_name, self.factory.poem)

    def sendRequest(self, xform_name, poem):
        self.sendString(xform_name + '.' + poem)

    def stringReceived(self, s):
        self.transport.loseConnection()
        self.poemReceived(s)

    def poemReceived(self, poem):
        self.factory.handlePoem(poem)


class TransformClientFactory(ClientFactory):

    protocol = TransformClientProtocol

    def __init__(self, xform_name, poem):
        self.xform_name = xform_name
        self.poem = poem
        self.deferred = defer.Deferred()

    def handlePoem(self, poem):
        d, self.deferred = self.deferred, None
        d.callback(poem)

    def clientConnectionLost(self, _, reason):
        if self.deferred is not None:
            d, self.deferred = self.deferred, None
            d.errback(reason)

    clientConnectionFailed = clientConnectionLost


class TransformProxy(object):
    """
I proxy requests to a transformation service.
"""

    def __init__(self, host, port):
        self.host = host
        self.port = port

    def xform(self, xform_name, poem):
        factory = TransformClientFactory(xform_name, poem)
        from twisted.internet import reactor
        reactor.connectTCP(self.host, self.port, factory)
        return factory.deferred


def get_poetry(host, port):
    """
Download a poem from the given host and port. This function
returns a Deferred which will be fired with the complete text of
the poem or a Failure if the poem could not be downloaded.
"""
    d = defer.Deferred()
    from twisted.internet import reactor
    factory = PoetryClientFactory(d)
    reactor.connectTCP(host, port, factory)
    return d


def poetry_main():
    addresses = parse_args()

    xform_addr = addresses.pop(0)

    proxy = TransformProxy(*xform_addr)

    from twisted.internet import reactor

    poems = []
    errors = []

    def try_to_cummingsify(poem):
        d = proxy.xform('cummingsify', poem)

        def fail(err):
            print >>sys.stderr, 'Cummingsify failed!'
            return poem

        return d.addErrback(fail)

    def got_poem(poem):
        print poem
        poems.append(poem)

    def poem_failed(err):
        print >>sys.stderr, 'The poem download failed.'
        errors.append(err)

    def poem_done(_):
        if len(poems) + len(errors) == len(addresses):
            reactor.stop()

    for address in addresses:
        host, port = address
        d = get_poetry(host, port)
        d.addCallback(try_to_cummingsify)
        d.addCallbacks(got_poem, poem_failed)
        d.addBoth(poem_done)

    reactor.run()


if __name__ == '__main__':
    poetry_main()



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值