def deferredGenerator(f): """ deferredGenerator and waitForDeferred help you write Deferred-using code that looks like a regular sequential function. If your code has a minimum requirement of Python 2.5, consider the use of L{inlineCallbacks} instead, which can accomplish the same thing in a more concise manner. There are two important functions involved: waitForDeferred, and deferredGenerator. They are used together, like this:: def thingummy(): thing = waitForDeferred(makeSomeRequestResultingInDeferred()) yield thing thing = thing.getResult() print thing #the result! hoorj! thingummy = deferredGenerator(thingummy) waitForDeferred returns something that you should immediately yield; when your generator is resumed, calling thing.getResult() will either give you the result of the Deferred if it was a success, or raise an exception if it was a failure. Calling C{getResult} is B{absolutely mandatory}. If you do not call it, I{your program will not work}. deferredGenerator takes one of these waitForDeferred-using generator functions and converts it into a function that returns a Deferred. The result of the Deferred will be the last value that your generator yielded unless the last value is a waitForDeferred instance, in which case the result will be C{None}. If the function raises an unhandled exception, the Deferred will errback instead. Remember that 'return result' won't work; use 'yield result; return' in place of that. Note that not yielding anything from your generator will make the Deferred result in None. Yielding a Deferred from your generator is also an error condition; always yield waitForDeferred(d) instead. The Deferred returned from your deferred generator may also errback if your generator raised an exception. For example:: def thingummy(): thing = waitForDeferred(makeSomeRequestResultingInDeferred()) yield thing thing = thing.getResult() if thing == 'I love Twisted': # will become the result of the Deferred yield 'TWISTED IS GREAT!' return else: # will trigger an errback raise Exception('DESTROY ALL LIFE') thingummy = deferredGenerator(thingummy) Put succinctly, these functions connect deferred-using code with this 'fake blocking' style in both directions: waitForDeferred converts from a Deferred to the 'blocking' style, and deferredGenerator converts from the 'blocking' style to a Deferred. """ def unwindGenerator(*args, **kwargs): return _deferGenerator(f(*args, **kwargs), Deferred()) return mergeFunctionMetadata(f, unwindGenerator)
def inlineCallbacks(f): """ WARNING: this function will not work in Python 2.4 and earlier! inlineCallbacks helps you write Deferred-using code that looks like a regular sequential function. This function uses features of Python 2.5 generators. If you need to be compatible with Python 2.4 or before, use the L{deferredGenerator} function instead, which accomplishes the same thing, but with somewhat more boilerplate. For example:: def thingummy(): thing = yield makeSomeRequestResultingInDeferred() print thing #the result! hoorj! thingummy = inlineCallbacks(thingummy) When you call anything that results in a Deferred, you can simply yield it; your generator will automatically be resumed when the Deferred's result is available. The generator will be sent the result of the Deferred with the 'send' method on generators, or if the result was a failure, 'throw'. Your inlineCallbacks-enabled generator will return a Deferred object, which will result in the return value of the generator (or will fail with a failure object if your generator raises an unhandled exception). Note that you can't use 'return result' to return a value; use 'returnValue(result)' instead. Falling off the end of the generator, or simply using 'return' will cause the Deferred to have a result of None. The Deferred returned from your deferred generator may errback if your generator raised an exception:: def thingummy(): thing = yield makeSomeRequestResultingInDeferred() if thing == 'I love Twisted': # will become the result of the Deferred returnValue('TWISTED IS GREAT!') else: # will trigger an errback raise Exception('DESTROY ALL LIFE') thingummy = inlineCallbacks(thingummy) """ def unwindGenerator(*args, **kwargs): return _inlineCallbacks(None, f(*args, **kwargs), Deferred()) return mergeFunctionMetadata(f, unwindGenerator)