Search code examples
pythonasynchronoustwisted

Strange behaviour when trying to implement twisted deferredlist


I'm new to twisted and trying to build a simple POC, however I keep getting the following error.

running
[]
running
[]
running
[]
running
[]
running
[]
Traceback (most recent call last):
  File "./poc.py", line 23, in <module>
    dl = defer.DeferredList([tasks])
  File "/usr/local/lib/python2.7/site-packages/twisted/internet/defer.py", line 820, in __init__
    deferred.addCallbacks(self._cbDeferred, self._cbDeferred,
AttributeError: 'list' object has no attribute 'addCallbacks'

I have been reading through the relevant twisted documentation and guides and can't seem to work out what I'm doing wrong. Does anybody know what is causing this exception?

#!/usr/bin/env python
from twisted.internet import defer

def task_to_run(data):
  print 'running'
  print data

def finished(res):
  print 'finished', res

tasks = []

for x in range(0,5):
  d = defer.Deferred()      
  d.addCallback(task_to_run)
  tasks.append(d)

dl = defer.DeferredList([tasks])
dl.addCallback(finished) 

Solution

  • Assuming that task_to_run really is something that is running async, then @Elisha was pointing you at the issue (your example code lists the list and it shouldn't). So runnable code looks like:

    #!/usr/bin/env python
    from twisted.internet import defer
    
    def task_to_run(data):
      print 'running'
      print data
    
    def finished(res):
      print 'finished', res
    
    tasks = []
    
    for x in range(0,5):
      d = defer.Deferred()      
      d.addCallback(task_to_run)
      d.callback("blah - " + str(x)) # Fire them as I make them
      tasks.append(d)
    
    dl = defer.DeferredList(tasks)
    
    dl.addCallback(finished) 
    

    With output of:

    % ./deferredList.py
    running
    blah - 0
    running
    blah - 1
    running
    blah - 2
    running
    blah - 3
    running
    blah - 4
    finished [(True, None), (True, None), (True, None), (True, None), (True, None)]
    

    BTW very nice docs on DeferredLists are at: Deferred Reference


    But if the tasks aren't actually running async then you really could just make one defer queue:

    #!/usr/bin/env python
    from twisted.internet import defer
    
    def task_to_run(data):
      print 'running'
      print data
      return data
    
    def finished(res):
      print 'finished', res
    
    d = defer.Deferred()      
    
    for x in range(0,5):
      d.addCallback(task_to_run)
    
    d.addCallback(finished) 
    
    d.callback("blah") # Fire the deferred
    

    Runs as:

    % ./deferred.py
    running
    blah
    running
    blah
    running
    blah
    running
    blah
    running
    blah
    finished blah