|  | @@ -29,18 +29,26 @@ from celery.loaders import get_loader_cls
 | 
	
		
			
				|  |  |  from celery.local import PromiseProxy, maybe_evaluate
 | 
	
		
			
				|  |  |  from celery.utils.functional import first
 | 
	
		
			
				|  |  |  from celery.utils.imports import instantiate, symbol_by_name
 | 
	
		
			
				|  |  | +from celery.utils.objects import mro_lookup
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  from .annotations import prepare as prepare_annotations
 | 
	
		
			
				|  |  |  from .builtins import shared_task, load_shared_tasks
 | 
	
		
			
				|  |  |  from .defaults import DEFAULTS, find_deprecated_settings
 | 
	
		
			
				|  |  |  from .registry import TaskRegistry
 | 
	
		
			
				|  |  | -from .utils import AppPickler, Settings, bugreport, _unpickle_app
 | 
	
		
			
				|  |  | +from .utils import (
 | 
	
		
			
				|  |  | +    AppPickler, Settings, bugreport, _unpickle_app, _unpickle_app_v2,
 | 
	
		
			
				|  |  | +)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  DEFAULT_FIXUPS = (
 | 
	
		
			
				|  |  |      'celery.fixups.django:DjangoFixup',
 | 
	
		
			
				|  |  |  )
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +def app_has_custom(app, attr):
 | 
	
		
			
				|  |  | +    return mro_lookup(app.__class__, attr, stop=(Celery, object),
 | 
	
		
			
				|  |  | +                      monkey_patched=[__name__])
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  def _unpickle_appattr(reverse_name, args):
 | 
	
		
			
				|  |  |      """Given an attribute name and a list of args, gets
 | 
	
		
			
				|  |  |      the attribute from the current app and calls it."""
 | 
	
	
		
			
				|  | @@ -90,9 +98,14 @@ class Celery(object):
 | 
	
		
			
				|  |  |          if not isinstance(self._tasks, TaskRegistry):
 | 
	
		
			
				|  |  |              self._tasks = TaskRegistry(self._tasks or {})
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +        # If the class defins a custom __reduce_args__ we need to use
 | 
	
		
			
				|  |  | +        # the old way of pickling apps, which is pickling a list of
 | 
	
		
			
				|  |  | +        # args instead of the new way that pickles a dict of keywords.
 | 
	
		
			
				|  |  | +        self._using_v1_reduce = app_has_custom(self, '__reduce_args__')
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          # these options are moved to the config to
 | 
	
		
			
				|  |  |          # simplify pickling of the app object.
 | 
	
		
			
				|  |  | -        self._preconf = {}
 | 
	
		
			
				|  |  | +        self._preconf = changes or {}
 | 
	
		
			
				|  |  |          if broker:
 | 
	
		
			
				|  |  |              self._preconf['BROKER_URL'] = broker
 | 
	
		
			
				|  |  |          if include:
 | 
	
	
		
			
				|  | @@ -387,13 +400,34 @@ class Celery(object):
 | 
	
		
			
				|  |  |              type(self).__name__, self.main or '__main__', id(self))
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      def __reduce__(self):
 | 
	
		
			
				|  |  | +        if self._using_v1_reduce:
 | 
	
		
			
				|  |  | +            return self.__reduce_v1__()
 | 
	
		
			
				|  |  | +        return (_unpickle_app_v2, (self.__class__, self.__reduce_keys__()))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    def __reduce_v1__(self):
 | 
	
		
			
				|  |  |          # Reduce only pickles the configuration changes,
 | 
	
		
			
				|  |  |          # so the default configuration doesn't have to be passed
 | 
	
		
			
				|  |  |          # between processes.
 | 
	
		
			
				|  |  |          return (_unpickle_app, (self.__class__, self.Pickler)
 | 
	
		
			
				|  |  |                                + self.__reduce_args__())
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    def __reduce_keys__(self):
 | 
	
		
			
				|  |  | +        """Returns keyword arguments used to reconstruct the object
 | 
	
		
			
				|  |  | +        when unpickling."""
 | 
	
		
			
				|  |  | +        return {
 | 
	
		
			
				|  |  | +            'main': self.main,
 | 
	
		
			
				|  |  | +            'changes': self.conf.changes,
 | 
	
		
			
				|  |  | +            'loader': self.loader_cls,
 | 
	
		
			
				|  |  | +            'backend': self.backend_cls,
 | 
	
		
			
				|  |  | +            'amqp': self.amqp_cls,
 | 
	
		
			
				|  |  | +            'events': self.events_cls,
 | 
	
		
			
				|  |  | +            'log': self.log_cls,
 | 
	
		
			
				|  |  | +            'control': self.control_cls,
 | 
	
		
			
				|  |  | +            'accept_magic_kwargs': self.accept_magic_kwargs,
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      def __reduce_args__(self):
 | 
	
		
			
				|  |  | +        """Deprecated method, please use :meth:`__reduce_keys__` instead."""
 | 
	
		
			
				|  |  |          return (self.main, self.conf.changes, self.loader_cls,
 | 
	
		
			
				|  |  |                  self.backend_cls, self.amqp_cls, self.events_cls,
 | 
	
		
			
				|  |  |                  self.log_cls, self.control_cls, self.accept_magic_kwargs)
 |