|
@@ -78,7 +78,6 @@ class TaskType(type):
|
|
|
|
|
|
def __new__(cls, name, bases, attrs):
|
|
|
new = super(TaskType, cls).__new__
|
|
|
- app = attrs.get("app") or current_app
|
|
|
task_module = attrs.get("__module__") or "__main__"
|
|
|
|
|
|
if "__call__" in attrs:
|
|
@@ -89,6 +88,13 @@ class TaskType(type):
|
|
|
if attrs.pop("abstract", None) or not attrs.get("autoregister", True):
|
|
|
return new(cls, name, bases, attrs)
|
|
|
|
|
|
+ # The 'app' attribute is now a property, with the real app located
|
|
|
+ # in the '_app' attribute. Previously this was a regular attribute,
|
|
|
+ # so we should support classes defining it.
|
|
|
+ app = attrs["_app"] = (attrs.pop("_app", None) or
|
|
|
+ attrs.pop("app", None) or
|
|
|
+ current_app)
|
|
|
+
|
|
|
# - Automatically generate missing/empty name.
|
|
|
autoname = False
|
|
|
if not attrs.get("name"):
|
|
@@ -125,9 +131,10 @@ class TaskType(type):
|
|
|
task_name = attrs["name"]
|
|
|
if task_name not in tasks:
|
|
|
task_cls = new(cls, name, bases, attrs)
|
|
|
- if autoname and task_module == "__main__" and task_cls.app.main:
|
|
|
- task_name = task_cls.name = '.'.join([task_cls.app.main, name])
|
|
|
+ if autoname and task_module == "__main__" and app.main:
|
|
|
+ task_name = task_cls.name = '.'.join([app.main, name])
|
|
|
tasks.register(task_cls)
|
|
|
+ task_cls.bind(app)
|
|
|
task = tasks[task_name].__class__
|
|
|
|
|
|
# decorate with annotations from config.
|
|
@@ -135,7 +142,9 @@ class TaskType(type):
|
|
|
return task
|
|
|
|
|
|
def __repr__(cls):
|
|
|
- return "<class Task of %s>" % (cls.app, )
|
|
|
+ if cls._app:
|
|
|
+ return "<class %s of %s>" % (cls.__name__, cls._app, )
|
|
|
+ return "<unbound %s>" % (cls.__name__, )
|
|
|
|
|
|
|
|
|
class BaseTask(object):
|
|
@@ -152,8 +161,11 @@ class BaseTask(object):
|
|
|
ErrorMail = ErrorMail
|
|
|
MaxRetriesExceededError = MaxRetriesExceededError
|
|
|
|
|
|
+ #: Execution strategy used, or the qualified name of one.
|
|
|
+ Strategy = "celery.worker.strategy:default"
|
|
|
+
|
|
|
#: The application instance associated with this task class.
|
|
|
- app = None
|
|
|
+ _app = None
|
|
|
|
|
|
#: Name of the task.
|
|
|
name = None
|
|
@@ -274,8 +286,56 @@ class BaseTask(object):
|
|
|
#: Default task expiry time.
|
|
|
expires = None
|
|
|
|
|
|
- #: Execution strategy used, or the qualified name of one.
|
|
|
- Strategy = "celery.worker.strategy:default"
|
|
|
+ from_config = (
|
|
|
+ ("exchange_type", "CELERY_DEFAULT_EXCHANGE_TYPE"),
|
|
|
+ ("delivery_mode", "CELERY_DEFAULT_DELIVERY_MODE"),
|
|
|
+ ("send_error_emails", "CELERY_SEND_TASK_ERROR_EMAILS"),
|
|
|
+ ("error_whitelist", "CELERY_TASK_ERROR_WHITELIST"),
|
|
|
+ ("serializer", "CELERY_TASK_SERIALIZER"),
|
|
|
+ ("rate_limit", "CELERY_DEFAULT_RATE_LIMIT"),
|
|
|
+ ("track_started", "CELERY_TRACK_STARTED"),
|
|
|
+ ("acks_late", "CELERY_ACKS_LATE"),
|
|
|
+ ("ignore_result", "CELERY_IGNORE_RESULT"),
|
|
|
+ ("store_errors_even_if_ignored",
|
|
|
+ "CELERY_STORE_ERRORS_EVEN_IF_IGNORED"),
|
|
|
+ )
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def bind(cls, app):
|
|
|
+ cls._app = app
|
|
|
+ conf = app.conf
|
|
|
+
|
|
|
+ for attr_name, config_name in cls.from_config:
|
|
|
+ if getattr(cls, attr_name, None) is None:
|
|
|
+ setattr(cls, attr_name, conf[config_name])
|
|
|
+ cls.accept_magic_kwargs = app.accept_magic_kwargs
|
|
|
+ if cls.accept_magic_kwargs is None:
|
|
|
+ cls.accept_magic_kwargs = app.accept_magic_kwargs
|
|
|
+ if cls.backend is None:
|
|
|
+ cls.backend = app.backend
|
|
|
+
|
|
|
+ # e.g. PeriodicTask uses this to add itself to the PeriodicTask
|
|
|
+ # schedule.
|
|
|
+ cls.on_bound(app)
|
|
|
+
|
|
|
+ return app
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def on_bound(self, app):
|
|
|
+ """This method can be defined to do additional actions when the
|
|
|
+ task class is bound to an app."""
|
|
|
+ pass
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def _get_app(cls):
|
|
|
+ if cls._app is None:
|
|
|
+ cls.bind(current_app)
|
|
|
+ return cls._app
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def _set_app(cls, app):
|
|
|
+ cls.bind(app)
|
|
|
+ app = property(_get_app, _set_app)
|
|
|
|
|
|
def __reduce__(self):
|
|
|
return (_unpickle_task, (self.name, ), None)
|
|
@@ -291,7 +351,7 @@ class BaseTask(object):
|
|
|
def get_logger(self, loglevel=None, logfile=None, propagate=False,
|
|
|
**kwargs):
|
|
|
"""Get task-aware logger object."""
|
|
|
- return self.app.log.setup_task_logger(
|
|
|
+ return self._get_app().log.setup_task_logger(
|
|
|
loglevel=self.request.loglevel if loglevel is None else loglevel,
|
|
|
logfile=self.request.logfile if logfile is None else logfile,
|
|
|
propagate=propagate, task_name=self.name, task_id=self.request.id)
|
|
@@ -299,7 +359,8 @@ class BaseTask(object):
|
|
|
@classmethod
|
|
|
def establish_connection(self, connect_timeout=None):
|
|
|
"""Establish a connection to the message broker."""
|
|
|
- return self.app.broker_connection(connect_timeout=connect_timeout)
|
|
|
+ return self._get_app().broker_connection(
|
|
|
+ connect_timeout=connect_timeout)
|
|
|
|
|
|
@classmethod
|
|
|
def get_publisher(self, connection=None, exchange=None,
|
|
@@ -328,7 +389,7 @@ class BaseTask(object):
|
|
|
if exchange_type is None:
|
|
|
exchange_type = self.exchange_type
|
|
|
connection = connection or self.establish_connection(connect_timeout)
|
|
|
- return self.app.amqp.TaskPublisher(connection=connection,
|
|
|
+ return self._get_app().amqp.TaskPublisher(connection=connection,
|
|
|
exchange=exchange,
|
|
|
exchange_type=exchange_type,
|
|
|
routing_key=self.routing_key,
|
|
@@ -353,7 +414,7 @@ class BaseTask(object):
|
|
|
|
|
|
"""
|
|
|
connection = connection or self.establish_connection(connect_timeout)
|
|
|
- return self.app.amqp.TaskConsumer(connection=connection,
|
|
|
+ return self._get_app().amqp.TaskConsumer(connection=connection,
|
|
|
exchange=self.exchange,
|
|
|
routing_key=self.routing_key)
|
|
|
|
|
@@ -457,19 +518,20 @@ class BaseTask(object):
|
|
|
be replaced by a local :func:`apply` call instead.
|
|
|
|
|
|
"""
|
|
|
- router = self.app.amqp.Router(queues)
|
|
|
- conf = self.app.conf
|
|
|
+ app = self._get_app()
|
|
|
+ router = app.amqp.Router(queues)
|
|
|
+ conf = app.conf
|
|
|
|
|
|
if conf.CELERY_ALWAYS_EAGER:
|
|
|
return self.apply(args, kwargs, task_id=task_id, **options)
|
|
|
options = dict(extract_exec_options(self), **options)
|
|
|
options = router.route(options, self.name, args, kwargs)
|
|
|
|
|
|
- publish = publisher or self.app.amqp.publisher_pool.acquire(block=True)
|
|
|
+ publish = publisher or app.amqp.publisher_pool.acquire(block=True)
|
|
|
evd = None
|
|
|
if conf.CELERY_SEND_TASK_SENT_EVENT:
|
|
|
- evd = self.app.events.Dispatcher(channel=publish.channel,
|
|
|
- buffer_while_offline=False)
|
|
|
+ evd = app.events.Dispatcher(channel=publish.channel,
|
|
|
+ buffer_while_offline=False)
|
|
|
|
|
|
try:
|
|
|
task_id = publish.delay_task(self.name, args, kwargs,
|
|
@@ -583,15 +645,16 @@ class BaseTask(object):
|
|
|
:rtype :class:`celery.result.EagerResult`:
|
|
|
|
|
|
"""
|
|
|
+ app = self._get_app()
|
|
|
args = args or []
|
|
|
kwargs = kwargs or {}
|
|
|
task_id = options.get("task_id") or uuid()
|
|
|
retries = options.get("retries", 0)
|
|
|
- throw = self.app.either("CELERY_EAGER_PROPAGATES_EXCEPTIONS",
|
|
|
- options.pop("throw", None))
|
|
|
+ throw = app.either("CELERY_EAGER_PROPAGATES_EXCEPTIONS",
|
|
|
+ options.pop("throw", None))
|
|
|
|
|
|
# Make sure we get the task instance, not class.
|
|
|
- task = self.app._tasks[self.name]
|
|
|
+ task = app._tasks[self.name]
|
|
|
|
|
|
request = {"id": task_id,
|
|
|
"retries": retries,
|
|
@@ -629,8 +692,8 @@ class BaseTask(object):
|
|
|
:param task_id: Task id to get result for.
|
|
|
|
|
|
"""
|
|
|
- return self.app.AsyncResult(task_id, backend=self.backend,
|
|
|
- task_name=self.name)
|
|
|
+ return self._get_app().AsyncResult(task_id, backend=self.backend,
|
|
|
+ task_name=self.name)
|
|
|
|
|
|
def update_state(self, task_id=None, state=None, meta=None):
|
|
|
"""Update task state.
|