| 
					
				 | 
			
			
				@@ -21,6 +21,7 @@ from itertools import chain as _chain 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 from kombu.utils import cached_property, fxrange, reprcall, uuid 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 from celery._state import current_app, get_current_worker_task 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+from celery.result import GroupResult 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 from celery.utils.functional import ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     maybe_list, is_list, regen, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     chunks as _chunks, 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -368,6 +369,7 @@ class chain(Signature): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             self, 'celery.chain', (), {'tasks': tasks}, **options 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         self.subtask_type = 'chain' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self._frozen = None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def __call__(self, *args, **kwargs): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if self.tasks: 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -387,10 +389,14 @@ class chain(Signature): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         app = app or self.app 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         args = (tuple(args) + tuple(self.args) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 if args and not self.immutable else self.args) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        tasks, results = self.prepare_steps( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            args, self.tasks, root_id, link_error, app, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            task_id, group_id, chord, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        try: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            tasks, results = self._frozen 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        except (AttributeError, ValueError): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            tasks, results = self.prepare_steps( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                args, self.tasks, root_id, link_error, app, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                task_id, group_id, chord, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if results: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             # make sure we can do a link() and link_error() on a chain object. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             if link: 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -398,6 +404,12 @@ class chain(Signature): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             tasks[0].apply_async(**options) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             return results[-1] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def freeze(self, _id=None, group_id=None, chord=None, root_id=None): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        _, results = self._frozen = self.prepare_steps( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            (), self.tasks, root_id, None, self.app, _id, group_id, chord, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return results[-1] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def prepare_steps(self, args, tasks, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                       root_id=None, link_error=None, app=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                       last_task_id=None, group_id=None, chord_body=None, 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -665,6 +677,16 @@ class group(Signature): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         result = self.app.GroupResult( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             group_id, list(self._apply_tasks(tasks, producer, app, **options)), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        # - Special case of group(A.s() | group(B.s(), C.s())) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        # That is, group with single item that is a chain but the 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        # last task in that chain is a group. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        # 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        # We cannot actually support arbitrary GroupResults in chains, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        # but this special case we can. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if len(result) == 1 and isinstance(result[0], GroupResult): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            result = result[0] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         parent_task = get_current_worker_task() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if add_to_parent and parent_task: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             parent_task.add_trail(result) 
			 |