The folks in the twisted community found the most wonderful trick. Consider this typical example of event driven code. Consider this example (proabably easier to read bottom to top). The function enqueueTaskToDisplayURL depends on three callbacks to handle the usual eventualities (good, bad, wrapup).
from twisted.web import client from twisted.internet import reactor import sys def shutdown(*whatever): reactor.stop() def printPage(data): print data def printError(failure): print >> sys.stderr, "Error:", failure.getErrorMessage() def enqueueTaskToDisplayURL(url):
get = client.getPage(url) get.addCallbacks(printPage, errback=printError) get.addBoth(shutdown)
if __name__ == "__main__":
enqueueTaskToDisplayURL
(
"
http://example.com/
")
reactor.run()
The trick they found was to use generators of their promise abstraction (aka defer). So we can write that example as so:
from twisted.internet import defer from twisted.web import client from twisted.internet import reactor import sys @defer.inlineCallbacks def
enqueueTaskToDisplayURL
(url): try: data = yield client.getPage(url) print data except Exception, e: print >> sys.stderr, "Error:", e return; finally: reactor.stop() if __name__ == "__main__":
enqueueTaskToDisplayURL
("http://example.com/") reactor.run()
The clever hack is done by @defer.inlineCallbacks, which works because this function (notice the yield) is a generator. That just made be giggle.
I’ve only gotten into Python and Twisted in a part time way over the past few months. I’m probably committing all kinds of sins here.
Nice article. Last month I read another article you wrote, https://enthusiasm.cozy.org/archives/2009/02/listening-to-the-system – which also was cool – tying together AMQP and syslog, etc. Anyways, I wanted to point out that there is a great Twisted AMQP client lib called txAMQP (https://launchpad.net/txamqp) by Esteve Fernandez that might catch your interest. The examples and tests utilize inlineCallbacks all over the place, in very useful and clean ways. Check the source code.
Alex – Ha! It was in fact the routine at line #54 here http://bazaar.launchpad.net/~txamqpteam/txamqp/trunk/annotate/head%3A/src/examples/client.py that forced me puzzle out what they were doing with @defer.inlineCallbacks; and hence to giggle. That beauty has 8 yields in it.