@_oldStyle class Deferred: called = False#类变量,在实例中引用时会自动在实例中生成 paused = False _debugInfo = None _suppressAlreadyCalled = False # Are we currently running a user-installed callback? Meant to prevent # recursive running of callbacks when a reentrant call to add a callback is # used. _runningCallbacks = False # Keep this class attribute for now, for compatibility with code that # sets it directly. debug = False _chainedTo = None def __init__(self, canceller=None): self.callbacks = []#存放回调函数的,每个回调函数的参数是一样的 self._canceller = canceller if self.debug: self._debugInfo = DebugInfo() self._debugInfo.creator = traceback.format_stack()[:-1] def addCallbacks(self, callback, errback=None, callbackArgs=None, callbackKeywords=None, errbackArgs=None, errbackKeywords=None): """ Add a pair of callbacks (success and error) to this L{Deferred}. These will be executed when the 'master' callback is run. @return: C{self}. @rtype: a L{Deferred} """ assert callable(callback) assert errback is None or callable(errback) cbs = ((callback, callbackArgs, callbackKeywords), (errback or (passthru), errbackArgs, errbackKeywords)) self.callbacks.append(cbs) if self.called:#如果已经回调了,就运行回调 self._runCallbacks() return self def addCallback(self, callback, *args, **kw): """ Convenience method for adding just a callback. See L{addCallbacks}. """ return self.addCallbacks(callback, callbackArgs=args, callbackKeywords=kw) def addErrback(self, errback, *args, **kw): """ Convenience method for adding just an errback. See L{addCallbacks}. """ return self.addCallbacks(passthru, errback, errbackArgs=args, errbackKeywords=kw) def addBoth(self, callback, *args, **kw): """ Convenience method for adding a single callable as both a callback and an errback. See L{addCallbacks}. """ return self.addCallbacks(callback, callback, callbackArgs=args, errbackArgs=args, callbackKeywords=kw, errbackKeywords=kw) def addTimeout(self, timeout, clock, onTimeoutCancel=None): timedOut = [False] def timeItOut(): timedOut[0] = True self.cancel() delayedCall = clock.callLater(timeout, timeItOut) def convertCancelled(value): # if C{deferred} was timed out, call the translation function, # if provdied, otherwise just use L{cancelledToTimedOutError} if timedOut[0]: toCall = onTimeoutCancel or _cancelledToTimedOutError return toCall(value, timeout) return value self.addBoth(convertCancelled) def cancelTimeout(result): # stop the pending call to cancel the deferred if it's been fired if delayedCall.active(): delayedCall.cancel() return result self.addBoth(cancelTimeout) return self def chainDeferred(self, d): d._chainedTo = self#把自己链接到d return self.addCallbacks(d.callback, d.errback) def callback(self, result): assert not isinstance(result, Deferred) self._startRunCallbacks(result) def errback(self, fail=None): if fail is None: fail = failure.Failure(captureVars=self.debug) elif not isinstance(fail, failure.Failure): fail = failure.Failure(fail) self._startRunCallbacks(fail) def pause(self):#在runcallback时会判断paused是否为0,不为0的话会直接返回 """ Stop processing on a L{Deferred} until L{unpause}() is called. """ self.paused = self.paused + 1 def unpause(self): """ Process all callbacks made since L{pause}() was called. """ self.paused = self.paused - 1 if self.paused: return if self.called: self._runCallbacks() def cancel(self): if not self.called: canceller = self._canceller if canceller: canceller(self) else: # Arrange to eat the callback that will eventually be fired # since there was no real canceller. self._suppressAlreadyCalled = True if not self.called: # There was no canceller, or the canceller didn't call # callback or errback. self.errback(failure.Failure(CancelledError())) elif isinstance(self.result, Deferred): # Waiting for another deferred -- cancel it instead. self.result.cancel() def _startRunCallbacks(self, result): if self.called: if self._suppressAlreadyCalled: self._suppressAlreadyCalled = False return if self.debug: if self._debugInfo is None: self._debugInfo = DebugInfo() extra = "\n" + self._debugInfo._getDebugTracebacks() raise AlreadyCalledError(extra) raise AlreadyCalledError if self.debug: if self._debugInfo is None: self._debugInfo = DebugInfo() self._debugInfo.invoker = traceback.format_stack()[:-2] self.called = True self.result = result#为结果赋值 self._runCallbacks() def _continuation(self):#回调函数返回deferred时,放置一个标志callback。 """ Build a tuple of callback and errback with L{_CONTINUE}. """ return ((_CONTINUE, (self,), None), (_CONTINUE, (self,), None)) def _runCallbacks(self): if self._runningCallbacks:#如果正在运行回调,就返回 # Don't recursively run callbacks return # Keep track of all the Deferreds encountered while propagating results # up a chain. The way a Deferred gets onto this stack is by having # added its _continuation() to the callbacks list of a second Deferred # and then that second Deferred being fired. ie, if ever had _chainedTo # set to something other than None, you might end up on this stack. chain = [self]#chain队列 while chain: current = chain[-1]#current为自己 if current.paused: # This Deferred isn't going to produce a result at all. All the # Deferreds up the chain waiting on it will just have to... # wait. return finished = True current._chainedTo = None while current.callbacks: item = current.callbacks.pop(0)#从回调队列的第一开始调用,调用完后就从队列中remove callback, args, kw = item[ isinstance(current.result, failure.Failure)]#根据result选择callback,还是errback args = args or () kw = kw or {} # Avoid recursion if we can. if callback is _CONTINUE:#说明该deferred对象有前级,要进入前级的调用 # Give the waiting Deferred our current result and then # forget about that result ourselves. chainee = args[0] chainee.result = current.result current.result = None # Making sure to update _debugInfo if current._debugInfo is not None: current._debugInfo.failResult = None chainee.paused -= 1#前级暂停数减1 chain.append(chainee)#退出当前callback循环 # Delay cleaning this Deferred and popping it from the chain # until after we've dealt with chainee. finished = False break try: current._runningCallbacks = True#在运行标志 try: current.result = callback(current.result, *args, **kw) if current.result is current: warnAboutFunction( callback, "Callback returned the Deferred " "it was attached to; this breaks the " "callback chain and will raise an " "exception in the future.") finally: current._runningCallbacks = False#取消在运行标志 except: # Including full frame information in the Failure is quite # expensive, so we avoid it unless self.debug is set. current.result = failure.Failure(captureVars=self.debug) else: if isinstance(current.result, Deferred):#返回结果是deferred # The result is another Deferred. If it has a result, # we can take it and keep going. resultResult = getattr(current.result, 'result', _NO_RESULT) if resultResult is _NO_RESULT or isinstance(resultResult, Deferred) or current.result.paused: # Nope, it didn't. Pause and chain. current.pause()#要暂停当前defer current._chainedTo = current.result # Note: current.result has no result, so it's not # running its callbacks right now. Therefore we can # append to the callbacks list directly instead of # using addCallbacks. current.result.callbacks.append(current._continuation())#为结果回调 break else: # Yep, it did. Steal it. current.result.result = None # Make sure _debugInfo's failure state is updated. if current.result._debugInfo is not None: current.result._debugInfo.failResult = None current.result = resultResult if finished: # As much of the callback chain - perhaps all of it - as can be # processed right now has been. The current Deferred is waiting on # another Deferred or for more callbacks. Before finishing with it, # make sure its _debugInfo is in the proper state. if isinstance(current.result, failure.Failure): # Stash the Failure in the _debugInfo for unhandled error # reporting. current.result.cleanFailure() if current._debugInfo is None: current._debugInfo = DebugInfo() current._debugInfo.failResult = current.result else: # Clear out any Failure in the _debugInfo, since the result # is no longer a Failure. if current._debugInfo is not None: current._debugInfo.failResult = None # This Deferred is done, pop it from the chain and move back up # to the Deferred which supplied us with our result. chain.pop()#完成一个defer的callback def __str__(self): """ Return a string representation of this C{Deferred}. """ cname = self.__class__.__name__ result = getattr(self, 'result', _NO_RESULT) myID = id(self) if self._chainedTo is not None: result = ' waiting on Deferred at 0x%x' % (id(self._chainedTo),) elif result is _NO_RESULT: result = '' else: result = ' current result: %r' % (result,) return "<%s at 0x%x%s>" % (cname, myID, result) __repr__ = __str__ def __iter__(self): return self def send(self, value=None): if self.paused: # If we're paused, we have no result to give return self result = getattr(self, 'result', _NO_RESULT) if result is _NO_RESULT: return self if isinstance(result, failure.Failure): # Clear the failure on debugInfo so it doesn't raise "unhandled # exception" self._debugInfo.failResult = None raise result.value else: raise StopIteration(result) # For PEP-492 support (async/await) __await__ = __iter__ __next__ = send
def _inlineCallbacks(result, g, deferred): """ See L{inlineCallbacks}. """ # This function is complicated by the need to prevent unbounded recursion # arising from repeatedly yielding immediately ready deferreds. This while # loop and the waiting variable solve that by manually unfolding the # recursion. waiting = [True, # waiting for result? None] # result while 1: try: # Send the last result back as the result of the yield expression. isFailure = isinstance(result, failure.Failure) if isFailure: result = result.throwExceptionIntoGenerator(g) else: result = g.send(result) except StopIteration as e: # fell off the end, or "return" statement deferred.callback(getattr(e, "value", None)) return deferred except _DefGen_Return as e: # returnValue() was called; time to give a result to the original # Deferred. First though, let's try to identify the potentially # confusing situation which results when returnValue() is # accidentally invoked from a different function, one that wasn't # decorated with @inlineCallbacks. # The traceback starts in this frame (the one for # _inlineCallbacks); the next one down should be the application # code. appCodeTrace = exc_info()[2].tb_next if isFailure: # If we invoked this generator frame by throwing an exception # into it, then throwExceptionIntoGenerator will consume an # additional stack frame itself, so we need to skip that too. appCodeTrace = appCodeTrace.tb_next # Now that we've identified the frame being exited by the # exception, let's figure out if returnValue was called from it # directly. returnValue itself consumes a stack frame, so the # application code will have a tb_next, but it will *not* have a # second tb_next. if appCodeTrace.tb_next.tb_next: # If returnValue was invoked non-local to the frame which it is # exiting, identify the frame that ultimately invoked # returnValue so that we can warn the user, as this behavior is # confusing. ultimateTrace = appCodeTrace while ultimateTrace.tb_next.tb_next: ultimateTrace = ultimateTrace.tb_next filename = ultimateTrace.tb_frame.f_code.co_filename lineno = ultimateTrace.tb_lineno warnings.warn_explicit( "returnValue() in %r causing %r to exit: " "returnValue should only be invoked by functions decorated " "with inlineCallbacks" % ( ultimateTrace.tb_frame.f_code.co_name, appCodeTrace.tb_frame.f_code.co_name), DeprecationWarning, filename, lineno) deferred.callback(e.value) return deferred except: deferred.errback() return deferred if isinstance(result, Deferred): # a deferred was yielded, get the result. def gotResult(r): if waiting[0]: waiting[0] = False waiting[1] = r else: _inlineCallbacks(r, g, deferred) result.addBoth(gotResult) if waiting[0]: # Haven't called back yet, set flag so that we get reinvoked # and return from the loop waiting[0] = False return deferred result = waiting[1] # Reset waiting to initial values for next loop. gotResult uses # waiting, but this isn't a problem because gotResult is only # executed once, and if it hasn't been executed yet, the return # branch above would have been taken. waiting[0] = True waiting[1] = None return deferred