views:

81

answers:

1

The scenario I want two different protocols interact with each other is as below:

A and B is two different protocols. First A will interact with the server and retrieve some values. Only after A finishes retrieving the values , B will start to interact with the server.

Now my problem is that is there an elegant way to initial B when A retrieves the values.

Currently I just initial B in A's data process function. But i don't think that this is an elegant way.

What I mean an elegant way is that the initialization of B is done by a flow controller or something like that, but not another protocol.

Is there an elegant way? such using defered or any other things.

I'm just new to twisted, not knowing very much about defered....

Thank you very much!

+1  A: 

It sounds like you've gotten past the first hurdle - figuring out how to have A and B interact at all. That's good, since for most people that's the biggest conceptual challenge. As for making it elegant, if you're after an approach that keeps your protocol code isolated from the application code driving it (ie, the "business logic"), there are several options. I'll give an example of one based on Deferreds.

Let's consider two POP3 clients. You want the first to retrieve a message list, then the second to retrieve the first message from the resulting list. This example

from twisted.internet import defer, protocol, reactor
from twisted.mail.pop3 import AdvancedPOP3Client

class MessageDownloader(object):
    def __init__(self, host, port, user, password):
        self.host = host
        self.port = port
        self.user = user
        self.password = password
        self.cc = ClientCreator(reactor, AdvancedPOP3Client)


    def connect(self):
        """
        Connect to the POP3 server and authenticate.  Return a Deferred
        which fires with the connected protocol instance.
        """
        connDeferred = self.cc.connect(self.host, self.port)
        def cbAuthenticate(proto):
            loginDeferred = proto.login(user, password)
            loginDeferred.addCallback(lambda ignored: proto)
            return loginDeferred
        connDeferred.addCallback(cbAuthenticate)
        return connDeferred


    def run(self):
        connDeferred = self.connect()
        connDeferred.addCallback(self.cbFirstConnection)
        return connDeferred


    def cbFirstConnection(self, firstProto):
        listDeferred = firstProto.listUID()

        def cbListed(uidList):
            connDeferred = self.connect()
            def cbConnected(secondProto):
                return secondProto.retrieve(uidList[0])
            connDeferred.addCallback(cbConnected)
        listDeferred.addCallback(cbListed)
        return listDeferred

if __name__ == '__main__':
    import sys
    MessageDownloader(*sys.argv[1:]).run()
    reactor.run()

Here, all of the logic about retrieving a list of UIDs and setting up a new connection to retrieve a message is separate from the actual protocol implementation (which is entirely in Twisted). The Deferreds returned from almost all of the APIs used here allow events to be connected up however your application wants.

Jean-Paul Calderone