Skip to content

Commit 974e06d

Browse files
committed
Merge branch 'master' of github.com:celery/celery
2 parents 962e589 + 5b38776 commit 974e06d

File tree

3 files changed

+80
-0
lines changed

3 files changed

+80
-0
lines changed

celery/app/base.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from collections import defaultdict, deque
1616
from copy import deepcopy
1717
from operator import attrgetter
18+
from functools import wraps
1819

1920
from amqp import promise
2021
try:
@@ -291,6 +292,20 @@ def _task_from_fun(self, fun, name=None, base=None, bind=False, **options):
291292
'__wrapped__': run}, **options))()
292293
self._tasks[task.name] = task
293294
task.bind(self) # connects task to this app
295+
296+
autoretry_for = tuple(options.get('autoretry_for', ()))
297+
retry_kwargs = options.get('retry_kwargs', {})
298+
299+
if autoretry_for and not hasattr(task, '_orig_run'):
300+
301+
@wraps(task.run)
302+
def run(*args, **kwargs):
303+
try:
304+
return task._orig_run(*args, **kwargs)
305+
except autoretry_for as exc:
306+
raise task.retry(exc=exc, **retry_kwargs)
307+
308+
task._orig_run, task.run = task.run, run
294309
else:
295310
task = self._tasks[name]
296311
return task

celery/tests/tasks/test_tasks.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,20 @@ def retry_task_customexc(self, arg1, arg2, kwarg=1, **kwargs):
100100
raise self.retry(countdown=0, exc=exc)
101101
self.retry_task_customexc = retry_task_customexc
102102

103+
@self.app.task(bind=True, autoretry_for=(ZeroDivisionError,),
104+
shared=False)
105+
def autoretry_task_no_kwargs(self, a, b):
106+
self.iterations += 1
107+
return a/b
108+
self.autoretry_task_no_kwargs = autoretry_task_no_kwargs
109+
110+
@self.app.task(bind=True, autoretry_for=(ZeroDivisionError,),
111+
retry_kwargs={'max_retries': 5}, shared=False)
112+
def autoretry_task(self, a, b):
113+
self.iterations += 1
114+
return a/b
115+
self.autoretry_task = autoretry_task
116+
103117

104118
class MyCustomException(Exception):
105119
"""Random custom exception."""
@@ -209,6 +223,18 @@ def test_max_retries_exceeded(self):
209223
result.get()
210224
self.assertEqual(self.retry_task.iterations, 2)
211225

226+
def test_autoretry_no_kwargs(self):
227+
self.autoretry_task_no_kwargs.max_retries = 3
228+
self.autoretry_task_no_kwargs.iterations = 0
229+
self.autoretry_task_no_kwargs.apply((1, 0))
230+
self.assertEqual(self.autoretry_task_no_kwargs.iterations, 4)
231+
232+
def test_autoretry(self):
233+
self.autoretry_task.max_retries = 3
234+
self.autoretry_task.iterations = 0
235+
self.autoretry_task.apply((1, 0))
236+
self.assertEqual(self.autoretry_task.iterations, 6)
237+
212238

213239
class test_canvas_utils(TasksCase):
214240

docs/userguide/tasks.rst

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,45 @@ override this default.
496496
raise self.retry(exc=exc, countdown=60) # override the default and
497497
# retry in 1 minute
498498
499+
Autoretrying
500+
------------
501+
502+
.. versionadded:: 3.2
503+
504+
Sometimes you may want to retry a task on particular exception. To do so,
505+
you should wrap a task body with `try-except` statement, for example:
506+
507+
.. code-block:: python
508+
509+
@app.task
510+
def div(a, b):
511+
try:
512+
return a / b
513+
except ZeroDivisionError as exc:
514+
raise div.retry(exc=exc)
515+
516+
This may not be acceptable all the time, since you may have a lot of such
517+
tasks.
518+
519+
Fortunately, you can tell Celery to automatically retry a task using
520+
`autoretry_for` argument in `~@Celery.task` decorator:
521+
522+
.. code-block:: python
523+
524+
@app.task(autoretry_for(ZeroDivisionError,))
525+
def div(a, b):
526+
return a / b
527+
528+
If you want to specify custom arguments for internal `~@Task.retry`
529+
call, pass `retry_kwargs` argument to `~@Celery.task` decorator:
530+
531+
.. code-block:: python
532+
533+
@app.task(autoretry_for=(ZeroDivisionError,),
534+
retry_kwargs={'max_retries': 5})
535+
def div(a, b):
536+
return a / b
537+
499538
.. _task-options:
500539

501540
List of Options

0 commit comments

Comments
 (0)