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 :)

_______________________________________________
Twisted-Python mailing list
Twisted-Python@twistedmatrix.com
http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python

Reply via email to