|  | @@ -10,6 +10,7 @@
 | 
	
		
			
				|  |  |  from __future__ import absolute_import
 | 
	
		
			
				|  |  |  from __future__ import with_statement
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +from collections import deque
 | 
	
		
			
				|  |  |  from itertools import starmap
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  from celery._state import get_current_worker_task
 | 
	
	
		
			
				|  | @@ -181,37 +182,48 @@ def add_chain_task(app):
 | 
	
		
			
				|  |  |          name = 'celery.chain'
 | 
	
		
			
				|  |  |          accept_magic_kwargs = False
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +        def prepare_steps(self, tasks, opts):
 | 
	
		
			
				|  |  | +            steps = deque(tasks)
 | 
	
		
			
				|  |  | +            next_step = prev_task = prev_res = None
 | 
	
		
			
				|  |  | +            tasks, results = [], []
 | 
	
		
			
				|  |  | +            while steps:
 | 
	
		
			
				|  |  | +                task = maybe_subtask(steps.popleft())
 | 
	
		
			
				|  |  | +                tid = task.options.get('task_id') or uuid()
 | 
	
		
			
				|  |  | +                res = task.type.AsyncResult(tid)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                # automatically upgrade group(..) | s to chord(group, s)
 | 
	
		
			
				|  |  | +                if isinstance(task, group):
 | 
	
		
			
				|  |  | +                    try:
 | 
	
		
			
				|  |  | +                        next_step = steps.popleft()
 | 
	
		
			
				|  |  | +                    except IndexError:
 | 
	
		
			
				|  |  | +                        next_step = None
 | 
	
		
			
				|  |  | +                if next_step is not None:
 | 
	
		
			
				|  |  | +                    task = chord(task, body=next_step, task_id=tid, **opts)
 | 
	
		
			
				|  |  | +                else:
 | 
	
		
			
				|  |  | +                    task = task.clone(task_id=tid, **opts)
 | 
	
		
			
				|  |  | +                if prev_task:
 | 
	
		
			
				|  |  | +                    # link previous task to this task.
 | 
	
		
			
				|  |  | +                    prev_task.link(task)
 | 
	
		
			
				|  |  | +                    # set the results parent attribute.
 | 
	
		
			
				|  |  | +                    res.parent = prev_res
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                results.append(res)
 | 
	
		
			
				|  |  | +                tasks.append(task)
 | 
	
		
			
				|  |  | +                prev_task, prev_res = task, res
 | 
	
		
			
				|  |  | +            return tasks, results
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          def apply_async(self, args=(), kwargs={}, **options):
 | 
	
		
			
				|  |  |              if self.app.conf.CELERY_ALWAYS_EAGER:
 | 
	
		
			
				|  |  |                  return self.apply(args, kwargs, **options)
 | 
	
		
			
				|  |  |              options.pop('publisher', None)
 | 
	
		
			
				|  |  |              group_id = options.pop('group_id', None)
 | 
	
		
			
				|  |  |              chord_id = options.pop('chord', None)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            def prepare_steps(tasks, opts):
 | 
	
		
			
				|  |  | -                i, size = 0, len(tasks);
 | 
	
		
			
				|  |  | -                while i < size:
 | 
	
		
			
				|  |  | -                    sig = maybe_subtask(tasks[i])
 | 
	
		
			
				|  |  | -                    task_id = sig.options.get('task_id')
 | 
	
		
			
				|  |  | -                    if isinstance(sig, group) and i + 1 < size:
 | 
	
		
			
				|  |  | -                        i += 1
 | 
	
		
			
				|  |  | -                        sig = chord(sig, body=tasks[i],
 | 
	
		
			
				|  |  | -                                    task_id=task_id or uuid(), **opts)
 | 
	
		
			
				|  |  | -                    else:
 | 
	
		
			
				|  |  | -                        sig = sig.clone(task_id=task_id or uuid(), **opts)
 | 
	
		
			
				|  |  | -                    yield sig
 | 
	
		
			
				|  |  | -                    i += 1
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            tasks = list(prepare_steps(kwargs['tasks'], options))
 | 
	
		
			
				|  |  | -            reduce(lambda a, b: a.link(b), tasks)
 | 
	
		
			
				|  |  | +            tasks, results = self.prepare_steps(kwargs['tasks'], options)
 | 
	
		
			
				|  |  |              if group_id:
 | 
	
		
			
				|  |  |                  tasks[-1].set(group_id=group_id)
 | 
	
		
			
				|  |  |              if chord_id:
 | 
	
		
			
				|  |  |                  tasks[-1].set(chord=chord_id)
 | 
	
		
			
				|  |  |              tasks[0].apply_async()
 | 
	
		
			
				|  |  | -            results = [task.type.AsyncResult(task.options['task_id'])
 | 
	
		
			
				|  |  | -                            for task in tasks]
 | 
	
		
			
				|  |  | -            reduce(lambda a, b: a.set_parent(b), reversed(results))
 | 
	
		
			
				|  |  |              return results[-1]
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          def apply(self, args=(), kwargs={}, **options):
 |