[Twisted-Python] Converting async to sync code (blocking until deferreds fire)
Phil Mayers
p.mayers at imperial.ac.uk
Mon Jul 21 07:12:34 EDT 2008
Jack Whitham wrote:
> Hi,
>
> What is the best way to convert asynchronous code (non-blocking,
> returning deferreds) to synchronous code (blocking)?
Don't.
>
> I have a client that is written using Twisted. Most of the methods
> within the client object return Deferreds because they send messages
> to a server and replies may take some time. This is ideal for the
> wxwidgets GUI that was originally intended to use the client. But
> now I want to write test scripts for the server that are synchronous.
>
> For example, the client provides the method "GetBitInfo" which returns
> a deferred. The deferred is called back with a list as its parameter
> after the server responds to a request. I would like to be able to
> do something like this:
>
>> def GotBitInfo(l): pass
>>
>> if __name__ == "__main__":
>> c = Client()
>>
>> d = c.GetBitInfo()
>> d.addCallback(GotBitInfo)
>> waitForDeferred(d)
>>
>> d = c.DoSomethingElse()
>
> In other words, how do I waitForDeferred(), i.e. block until a Deferred fires?
You've asked a complicated question, to which I'm sure you'll get other
answers. A very short answer is "you don't, the reactor must not block".
There are ways to achieve this, most of which make it *look* like the
reactor blocked when it really didn't.
I had the same desire at one point, to make the code "look" simple so
that others could use it. I have come to the conclusion that was a
mistake; trying to hide the underlying async behaviour can actually be
counter-productive.
You could use inlineCallbacks and python2.5 generators, which is a style
I swing between really liking and worrying about:
from twisted.internet import defer
@defer.inlineCallbacks
def main():
c = Client()
# execution of the generator is paused here until the
# deferred you've just yielded is callback'ed
r = yield c.GetBitInfo()
if r:
# "r" now contains the value of the callback
pass
# optionally
reactor.callLater(0.1, reactor.stop)
if __name__=='__main__':
from twisted.internet import reactor
reactor.addSystemEventTrigger('after', 'startup', main)
reactor.run()
There are other approaches using Stackless, greenlets and running the
reactor in a thread (shudder). For greenlets you might look at:
https://launchpad.net/corotwine/
More information about the Twisted-Python
mailing list