|
@@ -3,7 +3,7 @@ import warnings
|
|
from celery import registry
|
|
from celery import registry
|
|
from celery.app import app_or_default
|
|
from celery.app import app_or_default
|
|
from celery.datastructures import AttributeDict
|
|
from celery.datastructures import AttributeDict
|
|
-from celery.utils import gen_unique_id
|
|
|
|
|
|
+from celery.utils import cached_property, gen_unique_id
|
|
from celery.utils.compat import UserList
|
|
from celery.utils.compat import UserList
|
|
|
|
|
|
TASKSET_DEPRECATION_TEXT = """\
|
|
TASKSET_DEPRECATION_TEXT = """\
|
|
@@ -18,7 +18,6 @@ this so the syntax has been changed to:
|
|
ts = TaskSet(tasks=[
|
|
ts = TaskSet(tasks=[
|
|
%(cls)s.subtask(args1, kwargs1, options1),
|
|
%(cls)s.subtask(args1, kwargs1, options1),
|
|
%(cls)s.subtask(args2, kwargs2, options2),
|
|
%(cls)s.subtask(args2, kwargs2, options2),
|
|
- %(cls)s.subtask(args3, kwargs3, options3),
|
|
|
|
...
|
|
...
|
|
%(cls)s.subtask(argsN, kwargsN, optionsN),
|
|
%(cls)s.subtask(argsN, kwargsN, optionsN),
|
|
])
|
|
])
|
|
@@ -53,13 +52,11 @@ class subtask(AttributeDict):
|
|
|
|
|
|
"""
|
|
"""
|
|
|
|
|
|
- def __init__(self, task=None, args=None, kwargs=None, options=None,
|
|
|
|
- **extra):
|
|
|
|
|
|
+ def __init__(self, task=None, args=None, kwargs=None, options=None, **ex):
|
|
init = super(subtask, self).__init__
|
|
init = super(subtask, self).__init__
|
|
|
|
|
|
if isinstance(task, dict):
|
|
if isinstance(task, dict):
|
|
- # Use the values from a dict.
|
|
|
|
- return init(task)
|
|
|
|
|
|
+ return init(task) # works like dict(d)
|
|
|
|
|
|
# Also supports using task class/instance instead of string name.
|
|
# Also supports using task class/instance instead of string name.
|
|
try:
|
|
try:
|
|
@@ -68,7 +65,7 @@ class subtask(AttributeDict):
|
|
task_name = task
|
|
task_name = task
|
|
|
|
|
|
init(task=task_name, args=tuple(args or ()),
|
|
init(task=task_name, args=tuple(args or ()),
|
|
- kwargs=dict(kwargs or {}, **extra),
|
|
|
|
|
|
+ kwargs=dict(kwargs or {}, **ex),
|
|
options=options or {})
|
|
options=options or {})
|
|
|
|
|
|
def delay(self, *argmerge, **kwmerge):
|
|
def delay(self, *argmerge, **kwmerge):
|
|
@@ -81,7 +78,7 @@ class subtask(AttributeDict):
|
|
args = tuple(args) + tuple(self.args)
|
|
args = tuple(args) + tuple(self.args)
|
|
kwargs = dict(self.kwargs, **kwargs)
|
|
kwargs = dict(self.kwargs, **kwargs)
|
|
options = dict(self.options, **options)
|
|
options = dict(self.options, **options)
|
|
- return self.get_type().apply(args, kwargs, **options)
|
|
|
|
|
|
+ return self.type.apply(args, kwargs, **options)
|
|
|
|
|
|
def apply_async(self, args=(), kwargs={}, **options):
|
|
def apply_async(self, args=(), kwargs={}, **options):
|
|
"""Apply this task asynchronously."""
|
|
"""Apply this task asynchronously."""
|
|
@@ -89,42 +86,48 @@ class subtask(AttributeDict):
|
|
args = tuple(args) + tuple(self.args)
|
|
args = tuple(args) + tuple(self.args)
|
|
kwargs = dict(self.kwargs, **kwargs)
|
|
kwargs = dict(self.kwargs, **kwargs)
|
|
options = dict(self.options, **options)
|
|
options = dict(self.options, **options)
|
|
- return self.get_type().apply_async(args, kwargs, **options)
|
|
|
|
|
|
+ return self.type.apply_async(args, kwargs, **options)
|
|
|
|
|
|
def get_type(self):
|
|
def get_type(self):
|
|
- # For JSON serialization, the task class is lazily loaded,
|
|
|
|
|
|
+ return self.type
|
|
|
|
+
|
|
|
|
+ def __reduce__(self):
|
|
|
|
+ # for serialization, the task type is lazily loaded,
|
|
# and not stored in the dict itself.
|
|
# and not stored in the dict itself.
|
|
|
|
+ return (self.__class__, (dict(self), ), None)
|
|
|
|
+
|
|
|
|
+ def __repr__(self, kwformat=lambda i: "%s=%r" % i, sep=', '):
|
|
|
|
+ kw = self["kwargs"]
|
|
|
|
+ return "%s(%s%s%s)" % (self["task"], sep.join(map(repr, self["args"])),
|
|
|
|
+ kw and sep or "", sep.join(map(kwformat, kw.iteritems())))
|
|
|
|
+
|
|
|
|
+ @cached_property
|
|
|
|
+ def type(self):
|
|
return registry.tasks[self.task]
|
|
return registry.tasks[self.task]
|
|
|
|
|
|
|
|
|
|
class TaskSet(UserList):
|
|
class TaskSet(UserList):
|
|
"""A task containing several subtasks, making it possible
|
|
"""A task containing several subtasks, making it possible
|
|
- to track how many, or when all of the tasks has been completed.
|
|
|
|
|
|
+ to track how many, or when all of the tasks have been completed.
|
|
|
|
|
|
:param tasks: A list of :class:`subtask` instances.
|
|
:param tasks: A list of :class:`subtask` instances.
|
|
|
|
|
|
- .. attribute:: total
|
|
|
|
-
|
|
|
|
- Total number of subtasks in this task set.
|
|
|
|
-
|
|
|
|
Example::
|
|
Example::
|
|
|
|
|
|
- >>> from djangofeeds.tasks import RefreshFeedTask
|
|
|
|
- >>> from celery.task.sets import TaskSet, subtask
|
|
|
|
- >>> urls = ("http://cnn.com/rss",
|
|
|
|
- ... "http://bbc.co.uk/rss",
|
|
|
|
- ... "http://xkcd.com/rss")
|
|
|
|
- >>> subtasks = [RefreshFeedTask.subtask(kwargs={"feed_url": url})
|
|
|
|
- ... for url in urls]
|
|
|
|
- >>> taskset = TaskSet(tasks=subtasks)
|
|
|
|
|
|
+ >>> urls = ("http://cnn.com/rss", "http://bbc.co.uk/rss")
|
|
|
|
+ >>> taskset = TaskSet(refresh_feed.subtask((url, )) for url in urls)
|
|
>>> taskset_result = taskset.apply_async()
|
|
>>> taskset_result = taskset.apply_async()
|
|
- >>> list_of_return_values = taskset_result.join()
|
|
|
|
|
|
+ >>> list_of_return_values = taskset_result.join() # *expensive*
|
|
|
|
|
|
"""
|
|
"""
|
|
_task = None # compat
|
|
_task = None # compat
|
|
_task_name = None # compat
|
|
_task_name = None # compat
|
|
|
|
|
|
|
|
+ #: Total number of subtasks in this set.
|
|
|
|
+ total = None
|
|
|
|
+
|
|
def __init__(self, task=None, tasks=None, app=None, Publisher=None):
|
|
def __init__(self, task=None, tasks=None, app=None, Publisher=None):
|
|
|
|
+ self.app = app_or_default(app)
|
|
if task is not None:
|
|
if task is not None:
|
|
if hasattr(task, "__iter__"):
|
|
if hasattr(task, "__iter__"):
|
|
tasks = task
|
|
tasks = task
|
|
@@ -138,14 +141,13 @@ class TaskSet(UserList):
|
|
warnings.warn(TASKSET_DEPRECATION_TEXT % {
|
|
warnings.warn(TASKSET_DEPRECATION_TEXT % {
|
|
"cls": task.__class__.__name__},
|
|
"cls": task.__class__.__name__},
|
|
DeprecationWarning)
|
|
DeprecationWarning)
|
|
-
|
|
|
|
- self.app = app_or_default(app)
|
|
|
|
self.data = list(tasks or [])
|
|
self.data = list(tasks or [])
|
|
self.total = len(self.tasks)
|
|
self.total = len(self.tasks)
|
|
self.Publisher = Publisher or self.app.amqp.TaskPublisher
|
|
self.Publisher = Publisher or self.app.amqp.TaskPublisher
|
|
|
|
|
|
def apply_async(self, connection=None, connect_timeout=None,
|
|
def apply_async(self, connection=None, connect_timeout=None,
|
|
publisher=None):
|
|
publisher=None):
|
|
|
|
+ """Apply taskset."""
|
|
return self.app.with_default_connection(self._apply_async)(
|
|
return self.app.with_default_connection(self._apply_async)(
|
|
connection=connection,
|
|
connection=connection,
|
|
connect_timeout=connect_timeout,
|
|
connect_timeout=connect_timeout,
|
|
@@ -153,43 +155,13 @@ class TaskSet(UserList):
|
|
|
|
|
|
def _apply_async(self, connection=None, connect_timeout=None,
|
|
def _apply_async(self, connection=None, connect_timeout=None,
|
|
publisher=None):
|
|
publisher=None):
|
|
- """Run all tasks in the taskset.
|
|
|
|
-
|
|
|
|
- Returns a :class:`celery.result.TaskSetResult` instance.
|
|
|
|
-
|
|
|
|
- Example
|
|
|
|
-
|
|
|
|
- >>> ts = TaskSet(tasks=(
|
|
|
|
- ... RefreshFeedTask.subtask(["http://foo.com/rss"]),
|
|
|
|
- ... RefreshFeedTask.subtask(["http://bar.com/rss"]),
|
|
|
|
- ... ))
|
|
|
|
- >>> result = ts.apply_async()
|
|
|
|
- >>> result.taskset_id
|
|
|
|
- "d2c9b261-8eff-4bfb-8459-1e1b72063514"
|
|
|
|
- >>> result.subtask_ids
|
|
|
|
- ["b4996460-d959-49c8-aeb9-39c530dcde25",
|
|
|
|
- "598d2d18-ab86-45ca-8b4f-0779f5d6a3cb"]
|
|
|
|
- >>> result.waiting()
|
|
|
|
- True
|
|
|
|
- >>> time.sleep(10)
|
|
|
|
- >>> result.ready()
|
|
|
|
- True
|
|
|
|
- >>> result.successful()
|
|
|
|
- True
|
|
|
|
- >>> result.failed()
|
|
|
|
- False
|
|
|
|
- >>> result.join()
|
|
|
|
- [True, True]
|
|
|
|
-
|
|
|
|
- """
|
|
|
|
if self.app.conf.CELERY_ALWAYS_EAGER:
|
|
if self.app.conf.CELERY_ALWAYS_EAGER:
|
|
return self.apply()
|
|
return self.apply()
|
|
|
|
|
|
taskset_id = gen_unique_id()
|
|
taskset_id = gen_unique_id()
|
|
pub = publisher or self.Publisher(connection=connection)
|
|
pub = publisher or self.Publisher(connection=connection)
|
|
try:
|
|
try:
|
|
- results = [task.apply_async(taskset_id=taskset_id,
|
|
|
|
- publisher=pub)
|
|
|
|
|
|
+ results = [task.apply_async(taskset_id=taskset_id, publisher=pub)
|
|
for task in self.tasks]
|
|
for task in self.tasks]
|
|
finally:
|
|
finally:
|
|
if not publisher: # created by us.
|
|
if not publisher: # created by us.
|
|
@@ -198,13 +170,10 @@ class TaskSet(UserList):
|
|
return self.app.TaskSetResult(taskset_id, results)
|
|
return self.app.TaskSetResult(taskset_id, results)
|
|
|
|
|
|
def apply(self):
|
|
def apply(self):
|
|
- """Applies the taskset locally."""
|
|
|
|
- taskset_id = gen_unique_id()
|
|
|
|
-
|
|
|
|
- # this will be filled with EagerResults.
|
|
|
|
- results = [task.apply(taskset_id=taskset_id)
|
|
|
|
- for task in self.tasks]
|
|
|
|
- return self.app.TaskSetResult(taskset_id, results)
|
|
|
|
|
|
+ """Applies the taskset locally by blocking until all tasks return."""
|
|
|
|
+ setid = gen_unique_id()
|
|
|
|
+ return self.app.TaskSetResult(setid, [task.apply(taskset_id=setid)
|
|
|
|
+ for task in self.tasks])
|
|
|
|
|
|
@property
|
|
@property
|
|
def tasks(self):
|
|
def tasks(self):
|