Trying out a Retry decorator in Python
The Python wiki has a
Retry decorator example which retries calling a failure-prone function
using an
exponential backoff algorithm. I modified it slightly to check
for exceptions instead of a False return value to indicate
failure. Each time the decorated function throws an exception, the decorator
will wait a period of time and retry calling the function until the maximum
number of tries is used up. If the decorated function fails on the last try,
the exception will occur unhandled.
import time
def retry(ExceptionToCheck, tries=4, delay=3, backoff=2, logger=None):
"""Retry calling the decorated function using an exponential backoff.
http://www.saltycrane.com/blog/2009/11/trying-out-retry-decorator-python/
original from: http://wiki.python.org/moin/PythonDecoratorLibrary#Retry
:param ExceptionToCheck: the exception to check. may be a tuple of
excpetions to check
:type ExceptionToCheck: Exception or tuple
:param tries: number of times to try (not retry) before giving up
:type tries: int
:param delay: initial delay between retries in seconds
:type delay: int
:param backoff: backoff multiplier e.g. value of 2 will double the delay
each retry
:type backoff: int
:param logger: logger to use. If None, print
:type logger: logging.Logger instance
"""
def deco_retry(f):
def f_retry(*args, **kwargs):
mtries, mdelay = tries, delay
try_one_last_time = True
while mtries > 1:
try:
return f(*args, **kwargs)
try_one_last_time = False
break
except ExceptionToCheck, e:
msg = "%s, Retrying in %d seconds..." % (str(e), mdelay)
if logger:
logger.warning(msg)
else:
print msg
time.sleep(mdelay)
mtries -= 1
mdelay *= backoff
if try_one_last_time:
return f(*args, **kwargs)
return
return f_retry # true decorator
return deco_retry
Try an "always fail" case¶
@retry(Exception, tries=4)
def test_fail(text):
raise Exception("Fail")
test_fail("it works!")
Results:
Fail, Retrying in 3 seconds... Fail, Retrying in 6 seconds... Fail, Retrying in 12 seconds... Traceback (most recent call last): File "retry_decorator.py", line 47, intest_fail("it works!") File "retry_decorator.py", line 26, in f_retry f(*args, **kwargs) File "retry_decorator.py", line 33, in test_fail raise Exception("Fail") Exception: Fail
Try a "success" case¶
@retry(Exception, tries=4)
def test_success(text):
print "Success: ", text
test_success("it works!")
Results:
Success: it works!
Try a "random fail" case¶
import random
@retry(Exception, tries=4)
def test_random(text):
x = random.random()
if x < 0.5:
raise Exception("Fail")
else:
print "Success: ", text
test_random("it works!")
Results:
Fail, Retrying in 3 seconds... Success: it works!
Try handling multiple exceptions¶
Added 2010-04-27
import random
@retry((NameError, IOError), tries=20, delay=1, backoff=1)
def test_multiple_exceptions():
x = random.random()
if x < 0.40:
raise NameError("NameError")
elif x < 0.80:
raise IOError("IOError")
else:
raise KeyError("KeyError")
test_multiple_exceptions()
Results:
IOError, Retrying in 1 seconds... NameError, Retrying in 1 seconds... IOError, Retrying in 1 seconds... IOError, Retrying in 1 seconds... NameError, Retrying in 1 seconds... IOError, Retrying in 1 seconds... NameError, Retrying in 1 seconds... NameError, Retrying in 1 seconds... NameError, Retrying in 1 seconds... IOError, Retrying in 1 seconds... Traceback (most recent call last): File "retry_decorator.py", line 61, intest_multiple_exceptions("hello") File "retry_decorator.py", line 14, in f_retry f(*args, **kwargs) File "retry_decorator.py", line 56, in test_multiple_exceptions raise KeyError("KeyError") KeyError: 'KeyError'
Related posts
- Fabric post-run processing Python decorator — posted 2010-11-06
- Using a Python timeout decorator for uploading to S3 — posted 2010-04-27
- Two of the simplest Python decorator examples — posted 2010-03-09
3
Comments
—
Comments feed for this post
#2 Eliot commented on 2010-04-14:
Tiago: Thanks for the comment! I tried out your modified decorator just now.
Raising the exception as you do is more elegant than my method, but I don't like the new behavior. Before raising the exception the last time, it will print "Retrying in xx seconds..." and then sleep for xx seconds (without actually retrying the function).
Making it return the value that the decorated function returns is a good idea. I updated my post above.
#3 Jaskirat commented on 2011-01-14:
In your code the lines after "return f(args, *kwargs)" will never be called. These lines seem to be unreachable code:
try_one_last_time = False
break
You might want to remove them
Post a comment
About
I'm Eliot and this is my notepad for programming topics such as Python, Django, Ubuntu, Emacs, etc... more »
Search Blog
Tags
-
algorithms
(5)
-
aws
(9)
-
blogproject
(20)
-
c_cplusplus
(12)
-
cardstore
(8)
-
colinux
(2)
-
concurrency
(13)
-
conkeror
(2)
-
core
(2)
-
cygwin
(17)
-
datastructures
(14)
-
datetime
(4)
-
decorators
(4)
-
django
(40)
-
emacs
(22)
-
files_directories
(11)
-
git
(5)
-
hardware
(5)
-
install_setup
(8)
-
javascript
(3)
-
keyboard
(9)
-
matplotlib
(5)
-
mercurial
(4)
-
nginx
(2)
-
persistence
(5)
-
preferences
(7)
-
processes
(4)
-
pyqt
(18)
-
python
(144)
-
ratpoison
(3)
-
regexes
(6)
-
rsync
(3)
-
softwaretools
(17)
-
sql
(14)
-
ssh
(10)
-
subversion
(6)
-
twisted
(7)
-
ubuntu
(65)
-
urxvt
(5)
-
vxworks
(25)
-
webdev
(5)
-
wmii
(7)
Blogroll
- Adam Gomaa
- Alex Clemesha
- Amir Salihefendic
- Armin Ronacher
- David Beazley
- David Ziegler
- Duncan McGreggor
- Gareth Rushgrave
- Glyph Lefkowitz
- Guido van Rossum
- Ian Bicking
- Jacob Kaplan-Moss
- James Bennett
- James Tauber
- Jesper Noehr
- Marty Alchin
- Matt Harrison
- Nikolay Kolev
- Parand Darugar
- Peter Baumgartner
- Peter Bengtsson
- Rob Hudson
- Simon Willison
- Will McGugan
#1 Tiago Almeida commented on 2010-04-14:
This is great! Thanks for pointing this out. However i would modify it to return what the decorated function returns and throw (raise) the last exception given by the decorated function. Like this: