Martin Gergov
Martin Gergov

Reputation: 1660

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.

Upvotes: 1

Views: 2738

Answers (2)

Bula
Bula

Reputation: 1586

In this excellent post there are few examples of potential problems that can arise when using chainDeferred. So when using this method one should be careful.

Upvotes: 1

Jean-Paul Calderone
Jean-Paul Calderone

Reputation: 48335

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.

Upvotes: 1

Related Questions