Search code examples
pythonexceptiontwisteddeferred

Twisted chainDeferred not working as expected


I have a problem figuring out a somewhat simple twisted python code. From what I have red in the docs, the code here should work without Unhandled Error.

I get this:

HELLO!
HANDLED!
HANDLED 2!
Unhandled error in Deferred:
Unhandled Error
Traceback (most recent call last):
  File "package_tester.py", line 31, in <module>
    a().callback(2)
  File "/usr/local/lib/python2.7/dist-packages/twisted/internet/defer.py", line 368, in callback
    self._startRunCallbacks(result)
  File "/usr/local/lib/python2.7/dist-packages/twisted/internet/defer.py", line 464, in _startRunCallbacks
    self._runCallbacks()
--- <exception caught here> ---
  File "/usr/local/lib/python2.7/dist-packages/twisted/internet/defer.py", line 551, in _runCallbacks
    current.result = callback(current.result, *args, **kw)
  File "package_tester.py", line 5, in c
    raise Exception()
exceptions.Exception:

Isn't the failure from the chained deferred passed to end() errback ?

For some reason I can't comment below Bula's post
I managed to bypass the behaviour of 'unexpected1.py' by simply adding

@defer.inlineCallbacks
def sync_deferred(self, result, deferred):
        """
        Wait for a deferred to end before continuing.
        @param deferred: deferred which will be waited to finish so the chain
        can continue.
        @return: result from the deferred.
        """
        r = yield deferred
        defer.returnValue(r)

sync_deferred after every chainDeferred, in which the result from a child deferred needs to be "waited" so the parent can continue with this result.


Solution

  • From the documentation of Deferred.chainDeferred:

    When you chain a deferred d2 to another deferred d1 with d1.chainDeferred(d2),
    you are making d2 participate in the callback chain of d1. Thus any event that
    fires d1 will also fire d2.
    

    The error you supply to d1 (d in your example code) is passed on to d2 (l in your example code). This means that d2/l ends up with an error, which is passed to its first and only errback, new_f. new_f returns its argument, leaving d2/l with an error result, which is not handled.

    If you meant new_f to handle the failure, then you should make it ''not'' return a failure. From the Deferred howto:

    If the errback does returns a Failure or raise an exception, then that is passed
    to the next errback, and so on.