[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