[Twisted-Python] Deferred style question.

Anthony Baxter anthony at interlink.com.au
Fri Jan 7 05:13:15 EST 2005


After I posted the previous, two obvious points occurred to me:

  1. This is easy enough to modify to a generally useful 'DeferredCache'
      class
  2. Said class is a nice example of a decorator <wink>

So, see the attached for a DeferredCache decorator. It's not perfect,
because it currently doesn't do keyword arguments, and all arguments
to the operation must be hashable. This could be fixed, although it 
would make the code a bit nastier.

-- 
Anthony Baxter     <anthony at interlink.com.au>
It's never too late to have a happy childhood.
-------------- next part --------------

# XXX To be done: 
#     Caching relies on all arguments to the operation being cachable.
#     Caching doesn't handle keyword arguments.

from twisted.internet import defer


class DeferredCache:
    """ Wraps a call that returns a deferred in a cache. Any subsequent
        calls with the same argument will wait for the first call to 
        finish.
    """

    def __init__(self, op):
        self.op = op
        self.cache = {}

    def cb_triggerUserCallback(self, res, deferred):
        deferred.callback(res)
        return res

    def __call__(self, *args):
        # Currently not in progress - start it
        if not self.cache.has_key(args):
            # XXX check it returns a deferred?
            opdef = self.op(*args)
            self.cache[args] = opdef

        userdef = defer.Deferred()
        self.cache[args].addCallback(lambda x: 
                                    self.cb_triggerUserCallback(x, userdef))
        return userdef


@DeferredCache
def longRunningOperation(value):
    # Stub pointless operation - returns the value passed, after a 2s delay
    opdef = defer.Deferred()
    print "Operation called for", value
    reactor.callLater(2, lambda :opdef.callback(value))
    return opdef

# Alternatively, if you don't like decorators or are on < Python 2.4
#longRunningOperation = DeferredCache(longRunningOperation)


# Testing stuff


def gotAResult(res):
    print "I got a result!", res

def makeACall():
    print "making a call for 'x'"
    d = longRunningOperation('x')
    d.addCallback(gotAResult)

def startTheFun():
    makeACall()
    reactor.callLater(1, makeACall)
    reactor.callLater(4, makeACall)
    reactor.callLater(5, makeACall)

if __name__ == "__main__":
    from twisted.internet import reactor
    reactor.callLater(0, startTheFun)
    reactor.run()


More information about the Twisted-Python mailing list