Inline callbacks with Twisted and Python 2.5

A while ago I wrote about Defgen, which lets you use generator syntax to write Python code in a “blocking” format. I thought it was pretty cool, but it required some strange backflips to work right.

Yesterday, Glyph showed me something that was added to Twisted trunk a little while ago: inlineCallbacks. It’s the same concept, but because it uses the new yield syntax in Python 2.5, it’s even prettier.

Here’s the current source from doc/core/examples/ampclient.py:

from twisted.internet import reactor, defer
from twisted.internet.protocol import ClientCreator
from twisted.protocols import amp
from ampserver import Sum, Divide

def doMath():
    d1 = ClientCreator(reactor, amp.AMP).connectTCP(
        '127.0.0.1', 1234).addCallback(
            lambda p: p.callRemote(Sum, a=13, b=81)).addCallback(
                lambda result: result['total'])
    def trapZero(result):
        result.trap(ZeroDivisionError)
        print “Divided by zero: returning INF”
        return 1e1000
    d2 = ClientCreator(reactor, amp.AMP).connectTCP(
        ‘127.0.0.1′, 1234).addCallback(
            lambda p: p.callRemote(
                Divide,
                numerator=1234,
                denominator=0)
            ).addErrback(trapZero)
    def done(result):
        print ‘Done with math:’, result
    defer.DeferredList([d1, d2]).addCallback(done)

Here’s what it looks like using inlineCallbacks:

@defer.inlineCallbacks
def doMath():
    client = ClientCreator(reactor, amp.AMP)
    conn = yield client.connectTCP('127.0.0.1', 1234)
    sumResult = (yield conn.callRemote(Sum, a=13, b=81))['total']
    try:
        divideResult = yield conn.callRemote(
            Divide, numerator=1234, denominator=0)
    except ZeroDivisionError:
        print “Divided by zero: returning INF”
        divideResult = 1e1000
    result = [sumResult, divideResult]
    print ‘Done with math:’, result
    defer.returnValue(result)

It’s infinitely more legible. I’ve always hated the process of writing callback-driven procedures, just because the code no longer flows top-to-bottom. Even a simple client-side operation gets mangled (like above). But with inlineCallbacks, your code looks totally synchronous; it’s just executed asynchronously.

Too bad it’s only usable on Python 2.5. That means only Twisted-based applications can use it; the Twisted library itself won’t shed backwards-compatibility for a long time.

No comments yet.

Leave a Reply