[Twisted-Python] wired problem with deferreds

Petr Mifek pm-twisted-python at anapol.cz
Mon Feb 4 16:12:06 EST 2008


it looks to me like the chain of deferred callbacks is broken by this. 
At least i think deferreds aren't supposed to return the result of last 
callbacked function in the chain. If that is true, then your code fails 
on this:

You have d1, d2, cd. Before callbacking cd, the callback chains will 
look like this:

xd1 = half-done d1, callback chain: [cb1]
xd2 = half-done d2, callback chain: [cb2]
cd, callback chain: [req3, xd1, xd2]

the cd's chain processing will take req3 and it's return value, 'S' will 
give to the xd1 (_continue method). xd1 will unpause itself and give the 
'S' to cb1. The point is here, that although cb1 returns 'S', it is only 
relevant to xd1 aka d1 chain processing. The d1 deferred does return 
None. So that goes to next in cd's chain, the xd2. xd2 will give None to 
cb2 and there goes your output.

The deferred chaining works, because if some addCallback'ed' function 
returns a deferred, then the current deffered adds it's '_continue' 
method to that deferred's chain. That way it gets the result values and 
can process the chain further then.

The short answer is probably: You can return a deferred(s) from 
addCallbackeds methods and wait for them, but should only wait in one 
place for one deferred. If returning the same deferred in multiple 
places, only the first will get the result of that deferred chain (and i 
thint that this is certainly not the common use case ;) Think about some 
tree-like-pattern, that branches only one way. Otherwise it's really 
twisted ;)

Probably you should use an array of distinct deferreds in req3, although 
I'm not sure of the purpose of the code.

Hope this helps.


markus espenhain wrote:
> hi
> im stuck with the following deferred construct - the problem is that cb2
> is called with None instead of the expected argument
> from twisted.internet import defer
> cd = None
> def req1():
>     return defer.Deferred().addCallback(req2)
> def req2(a):
>     print 'req2', a
>     global cd
>     if not cd:
>         cd = defer.Deferred().addCallback(req3)
>     return cd
> def req3(a):
>     print 'req3', a
>     return a
> def cb1(a):
>     print 'cb1', a
>     return a
> def cb2(a):
>     print 'cb2', a
>     return a
> d1 = req1().addCallback(cb1)
> d1.callback('X')
> d2 = req1().addCallback(cb2)
> d2.callback('X')
> cd.callback('S')
> output:
> req2 X
> req2 X
> req3 S
> cb1 S
> cb2 None # <- ?
> req1 fetches a mapping for given arguments - req2 tries to create an
> object + additional resquests by the result of req1 - but these objects
> should only be created once - so far i didn't find an acceptable
> workaround for that - req3 applys some modifications but the problem
> occurs also without the step in req3 
> is there something i overlooked so that cb2 isn't called with the
> required arg?
> thanks
> markus

More information about the Twisted-Python mailing list