[Twisted-Python] inlineCallbacks cascading cancelling and more

Sergey Magafurov smagafurov at naumen.ru
Mon Aug 23 04:22:06 MDT 2010


Thanks to all!
Especially to Yaroslav Fedevych who explain me my misstake in my native 
language :)
I was wrong with deferreds usage.

Cascading cancelling of inlineCallbacks is still needed to me, but it 
can be realized with current Deferred API.

This way for example:

class InlineCallbacksManagerWithCascadeCancelling(object):
     _cancellation = False
     _wait_result = None

     def __init__(self):
         self.deferred = defer.Deferred()
         self.deferred.addBoth(self._cleanup)

     def _cleanup(self, result):
         if self._wait_result is not None:
             self._cancellation = True
             self._wait_result.cancel()
         self._wait_result = None
         self.deferred = None
         return result

     def _inlineCallbacks(self, result, g):
         """
         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

         deferred = self.deferred

         while 1:
             if self._cancellation:
                 g.close()
                 return

             try:
                 # Send the last result back as the result of the yield 
expression.
                 isFailure = isinstance(result, Failure)
                 if isFailure:
                     result = result.throwExceptionIntoGenerator(g)
                 else:
                     result = g.send(result)
             except StopIteration:
                 # fell off the end, or "return" statement
                 deferred.callback(None)
                 return deferred
             except defer._DefGen_Return, e:
                 # returnValue() was called; time to give a result to 
the original Deferred.
                 deferred.callback(e.value)
                 return deferred
             except:
                 deferred.errback()
                 return deferred

             if isinstance(result, defer.Deferred):
                 # a deferred was yielded, get the result.
                 def gotResult(r):
                     if waiting[0]:
                         waiting[0] = False
                         waiting[1] = r
                     else:
                         self._wait_result = None
                         self._inlineCallbacks(r, g)

                 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
                     self._wait_result = result
                     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

def inlineCallbacksWithCascadeCancelling(f):
     def unwind_generator(*args, **kwargs):
         manager = InlineCallbacksManagerWithCascadeCancelling(*args, 
**kwargs)
         return manager._inlineCallbacks(None, f(*args, **kwargs))
     return mergeFunctionMetadata(f, unwind_generator)

This inlineCallbacksWithCascadeCancelling cancels immediately "child" 
(wait result) deferred when "parent" deferred finished (canceled for 
example) and stops generator.

May be this behaviour must be default for inlineCallbacks (i.e. 
defer.inlineCallbacks = inlineCallbacksWithCascadeCancelling)?

I am happy with this behaviour :)

-------------- next part --------------
An HTML attachment was scrubbed...
URL: </pipermail/twisted-python/attachments/20100823/c80f0a36/attachment.html>


More information about the Twisted-Python mailing list