[Twisted-Python] inlineCallbacks cascading cancelling and more

Sergey Magafurov smagafurov at naumen.ru
Mon Aug 23 06:22:06 EDT 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()

     def _cleanup(self, result):
         if self._wait_result is not None:
             self._cancellation = True
         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 
         # 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:

                 # Send the last result back as the result of the yield 
                 isFailure = isinstance(result, Failure)
                 if isFailure:
                     result = result.throwExceptionIntoGenerator(g)
                     result = g.send(result)
             except StopIteration:
                 # fell off the end, or "return" statement
                 return deferred
             except defer._DefGen_Return, e:
                 # returnValue() was called; time to give a result to 
the original Deferred.
                 return deferred
                 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
                         self._wait_result = None
                         self._inlineCallbacks(r, g)

                 if waiting[0]:
                     # Haven't called back yet, set flag so that we get 
                     # 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, 
         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: http://twistedmatrix.com/pipermail/twisted-python/attachments/20100823/c80f0a36/attachment.htm 

More information about the Twisted-Python mailing list