[Twisted-Python] inlineCallbacksDecorator

Terry Jones terry at jon.es
Sat Jun 6 21:41:08 EDT 2009


I try to avoid using inlineCallbacks. There are two cases where I will
happily use it though: when I write a method that would need more than a
few callback functions, and, more importantly, when the logical flow of a
method is non-trivial (i.e., it depends on the returned values or errors of
several deferreds).

One inconvenience with inlineCallbacks is that you might have some kind of
processing you want done no matter how the function returns, or wherever an
error occurs. Two solutions here are 1) to put try/except calls around your
various yields, and/or to perhaps do something else with various callbacks
that might call defer.returnValue, or 2) expect each caller of your method
to deal with the result. I don't like the first of those much (depending on
the code), and don't like the second at all.

So I wrote a decorator specifically for inlineCallbacks decorated functions:

    from twisted.internet import defer

    def inlineCallbacksDecorator(callback, errback=defer.passthru):
        def wrap(f):
            def wrapper(*args, **kw):
                d = f(*args, **kw)
                if isinstance(d, defer.Deferred):
                    return d.addCallbacks(callback, errback,
                                          callbackArgs=args, callbackKeywords=kw,
                                          errbackArgs=args, errbackKeywords=kw)
                # We were used to decorate a non-inlineCallbacks function.
                raise Exception(
                    'Function %r was decorated with inlineCallbacksDecorator but '
                    'did not return a deferred (did you forget to also decorate '
                    'with inlineCallbacks?)' % f.__name__)
            return mergeFunctionMetadata(f, wrapper)
        return wrap

You can use it like this:

    def _cbok(result, a, b=None):
        print 'In _cbok: a=%r b=%r' % (a, b)
        return result

    def _cberr(failure, *args, **kw):
        pass
        # Do something....  and maybe also return the failure.

    @inlineCallbacksDecorator(_cbok, _cberr)
    @defer.inlineCallbacks
    def frog(a, b=None):
        print 'a = %r, b = %r' % (a, b)
        result = yield produceDeferred()
        # yield produceErr()
        defer.returnValue(a)


The nice/interesting thing about this is that the callback and errback
functions get called with the deferred result of calling frog (as decorated
by inlineCallbacks) *and* the original arguments passed to frog.  You can
of course ignore the original args if you're not interested in them.

You can argue that this doesn't really buy you anything. That's of course
true.  It's only a decorator that makes it neater to do certain things.
Logging, accounting and error processing come immediately to mind. Just
keeping code looking simpler/cleaner is enough of a reason for me.

After writing this, I realized it deals with what I was clumsily trying to
achieve here
http://twistedmatrix.com/pipermail/twisted-python/2009-April/019492.html
back in April.

Terry




More information about the Twisted-Python mailing list