|  | @@ -29,18 +29,26 @@ from celery.loaders import get_loader_cls
 | 
											
												
													
														|  |  from celery.local import PromiseProxy, maybe_evaluate
 |  |  from celery.local import PromiseProxy, maybe_evaluate
 | 
											
												
													
														|  |  from celery.utils.functional import first
 |  |  from celery.utils.functional import first
 | 
											
												
													
														|  |  from celery.utils.imports import instantiate, symbol_by_name
 |  |  from celery.utils.imports import instantiate, symbol_by_name
 | 
											
												
													
														|  | 
 |  | +from celery.utils.objects import mro_lookup
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  from .annotations import prepare as prepare_annotations
 |  |  from .annotations import prepare as prepare_annotations
 | 
											
												
													
														|  |  from .builtins import shared_task, load_shared_tasks
 |  |  from .builtins import shared_task, load_shared_tasks
 | 
											
												
													
														|  |  from .defaults import DEFAULTS, find_deprecated_settings
 |  |  from .defaults import DEFAULTS, find_deprecated_settings
 | 
											
												
													
														|  |  from .registry import TaskRegistry
 |  |  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 = (
 |  |  DEFAULT_FIXUPS = (
 | 
											
												
													
														|  |      'celery.fixups.django:DjangoFixup',
 |  |      '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):
 |  |  def _unpickle_appattr(reverse_name, args):
 | 
											
												
													
														|  |      """Given an attribute name and a list of args, gets
 |  |      """Given an attribute name and a list of args, gets
 | 
											
												
													
														|  |      the attribute from the current app and calls it."""
 |  |      the attribute from the current app and calls it."""
 | 
											
										
											
												
													
														|  | @@ -90,9 +98,14 @@ class Celery(object):
 | 
											
												
													
														|  |          if not isinstance(self._tasks, TaskRegistry):
 |  |          if not isinstance(self._tasks, TaskRegistry):
 | 
											
												
													
														|  |              self._tasks = TaskRegistry(self._tasks or {})
 |  |              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
 |  |          # these options are moved to the config to
 | 
											
												
													
														|  |          # simplify pickling of the app object.
 |  |          # simplify pickling of the app object.
 | 
											
												
													
														|  | -        self._preconf = {}
 |  | 
 | 
											
												
													
														|  | 
 |  | +        self._preconf = changes or {}
 | 
											
												
													
														|  |          if broker:
 |  |          if broker:
 | 
											
												
													
														|  |              self._preconf['BROKER_URL'] = broker
 |  |              self._preconf['BROKER_URL'] = broker
 | 
											
												
													
														|  |          if include:
 |  |          if include:
 | 
											
										
											
												
													
														|  | @@ -387,13 +400,34 @@ class Celery(object):
 | 
											
												
													
														|  |              type(self).__name__, self.main or '__main__', id(self))
 |  |              type(self).__name__, self.main or '__main__', id(self))
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |      def __reduce__(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,
 |  |          # Reduce only pickles the configuration changes,
 | 
											
												
													
														|  |          # so the default configuration doesn't have to be passed
 |  |          # so the default configuration doesn't have to be passed
 | 
											
												
													
														|  |          # between processes.
 |  |          # between processes.
 | 
											
												
													
														|  |          return (_unpickle_app, (self.__class__, self.Pickler)
 |  |          return (_unpickle_app, (self.__class__, self.Pickler)
 | 
											
												
													
														|  |                                + self.__reduce_args__())
 |  |                                + 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):
 |  |      def __reduce_args__(self):
 | 
											
												
													
														|  | 
 |  | +        """Deprecated method, please use :meth:`__reduce_keys__` instead."""
 | 
											
												
													
														|  |          return (self.main, self.conf.changes, self.loader_cls,
 |  |          return (self.main, self.conf.changes, self.loader_cls,
 | 
											
												
													
														|  |                  self.backend_cls, self.amqp_cls, self.events_cls,
 |  |                  self.backend_cls, self.amqp_cls, self.events_cls,
 | 
											
												
													
														|  |                  self.log_cls, self.control_cls, self.accept_magic_kwargs)
 |  |                  self.log_cls, self.control_cls, self.accept_magic_kwargs)
 |