def deferredGenerator(f): """ deferredGenerator and waitForDeferred help you write L{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: L{waitForDeferred}, and L{deferredGenerator}. They are used together, like this:: @deferredGenerator def thingummy(): thing = waitForDeferred(makeSomeRequestResultingInDeferred()) yield thing thing = thing.getResult() print thing #the result! hoorj! L{waitForDeferred} returns something that you should immediately yield; when your generator is resumed, calling C{thing.getResult()} will either give you the result of the L{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}. L{deferredGenerator} takes one of these waitForDeferred-using generator functions and converts it into a function that returns a L{Deferred}. The result of the L{Deferred} will be the last value that your generator yielded unless the last value is a L{waitForDeferred} instance, in which case the result will be C{None}. If the function raises an unhandled exception, the L{Deferred} will errback instead. Remember that C{return result} won't work; use C{yield result; return} in place of that. Note that not yielding anything from your generator will make the L{Deferred} result in C{None}. Yielding a L{Deferred} from your generator is also an error condition; always yield C{waitForDeferred(d)} instead. The L{Deferred} returned from your deferred generator may also errback if your generator raised an exception. For example:: @deferredGenerator 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') Put succinctly, these functions connect deferred-using code with this 'fake blocking' style in both directions: L{waitForDeferred} converts from a L{Deferred} to the 'blocking' style, and L{deferredGenerator} converts from the 'blocking' style to a L{Deferred}. """ def unwindGenerator(*args, **kwargs): return _deferGenerator(f(*args, **kwargs), Deferred()) return mergeFunctionMetadata(f, unwindGenerator)
def suppressWarnings(f, *suppressedWarnings): """ Wrap C{f} in a callable which suppresses the indicated warnings before invoking C{f} and unsuppresses them afterwards. If f returns a Deferred, warnings will remain suppressed until the Deferred fires. """ def warningSuppressingWrapper(*a, **kw): return runWithWarningsSuppressed(suppressedWarnings, f, *a, **kw) return tputil.mergeFunctionMetadata(f, warningSuppressingWrapper)
def deprecationDecorator(function): """ Decorator that marks C{function} as deprecated. """ warningString = getDeprecationWarningString(function, version) def deprecatedFunction(*args, **kwargs): warn(warningString, DeprecationWarning, stacklevel=2) return function(*args, **kwargs) deprecatedFunction = mergeFunctionMetadata(function, deprecatedFunction) _appendToDocstring(deprecatedFunction, _getDeprecationDocstring(version)) deprecatedFunction.deprecatedVersion = version return deprecatedFunction
def deprecationDecorator(function): """ Decorator that marks C{function} as deprecated. """ warningString = getDeprecationWarningString(function, version) def deprecatedFunction(*args, **kwargs): warn( warningString, DeprecationWarning, stacklevel=2) return function(*args, **kwargs) deprecatedFunction = mergeFunctionMetadata( function, deprecatedFunction) _appendToDocstring(deprecatedFunction, _getDeprecationDocstring(version)) deprecatedFunction.deprecatedVersion = version return deprecatedFunction
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:: @inlineCallBacks def thingummy(): thing = yield makeSomeRequestResultingInDeferred() print thing #the result! hoorj! When you call anything that results in a L{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 L{Deferred} with the 'send' method on generators, or if the result was a failure, 'throw'. Your inlineCallbacks-enabled generator will return a L{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 C{return result} to return a value; use C{returnValue(result)} instead. Falling off the end of the generator, or simply using C{return} will cause the L{Deferred} to have a result of C{None}. The L{Deferred} returned from your deferred generator may errback if your generator raised an exception:: @inlineCallbacks 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') """ def unwindGenerator(*args, **kwargs): return _inlineCallbacks(None, f(*args, **kwargs), Deferred()) return mergeFunctionMetadata(f, unwindGenerator)