-
-
Save jric/7421708 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# | |
# Copyright 2012 by Jeff Laughlin Consulting LLC | |
# | |
# Permission is hereby granted, free of charge, to any person obtaining a copy | |
# of this software and associated documentation files (the "Software"), to deal | |
# in the Software without restriction, including without limitation the rights | |
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
# copies of the Software, and to permit persons to whom the Software is | |
# furnished to do so, subject to the following conditions: | |
# | |
# The above copyright notice and this permission notice shall be included in | |
# all copies or substantial portions of the Software. | |
# | |
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
# SOFTWARE. | |
import sys | |
from time import sleep | |
def example_exc_handler(tries_remaining, exception, delay): | |
"""Example exception handler; prints a warning to stderr. | |
tries_remaining: The number of tries remaining. | |
exception: The exception instance which was raised. | |
""" | |
print >> sys.stderr, "Caught '%s', %d tries remaining, sleeping for %s seconds" % (exception, tries_remaining, delay) | |
def retries(max_tries, delay=1, backoff=2, exceptions=(Exception,), hook=None): | |
"""Function decorator implementing retrying logic. | |
delay: Sleep this many seconds * backoff * try number after failure | |
backoff: Multiply delay by this factor after each failure | |
exceptions: A tuple of exception classes; default (Exception,) | |
hook: A function with the signature myhook(tries_remaining, exception); | |
default None | |
The decorator will call the function up to max_tries times if it raises | |
an exception. | |
By default it catches instances of the Exception class and subclasses. | |
This will recover after all but the most fatal errors. You may specify a | |
custom tuple of exception classes with the 'exceptions' argument; the | |
function will only be retried if it raises one of the specified | |
exceptions. | |
Additionally you may specify a hook function which will be called prior | |
to retrying with the number of remaining tries and the exception instance; | |
see given example. This is primarily intended to give the opportunity to | |
log the failure. Hook is not called after failure if no retries remain. | |
""" | |
def dec(func): | |
def f2(*args, **kwargs): | |
mydelay = delay | |
tries = range(max_tries) | |
tries.reverse() | |
for tries_remaining in tries: | |
try: | |
return func(*args, **kwargs) | |
except exceptions as e: | |
if tries_remaining > 0: | |
if hook is not None: | |
hook(tries_remaining, e, mydelay) | |
sleep(mydelay) | |
mydelay = mydelay * backoff | |
else: | |
raise | |
else: | |
break | |
return f2 | |
return dec |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# | |
# Copyright 2012 by Jeff Laughlin Consulting LLC | |
# | |
# Permission is hereby granted, free of charge, to any person obtaining a copy | |
# of this software and associated documentation files (the "Software"), to deal | |
# in the Software without restriction, including without limitation the rights | |
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
# copies of the Software, and to permit persons to whom the Software is | |
# furnished to do so, subject to the following conditions: | |
# | |
# The above copyright notice and this permission notice shall be included in | |
# all copies or substantial portions of the Software. | |
# | |
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
# SOFTWARE. | |
from retry import retries, example_exc_handler | |
import nose.tools | |
from nose.tools import * | |
class MyException(Exception): | |
pass | |
n = 0 | |
@retries(3, hook=example_exc_handler) | |
def retry_me(): | |
global n | |
n += 1 | |
raise MyException('Foobar') | |
def test_retries(): | |
try: | |
retry_me() | |
except MyException, e: | |
pass | |
else: | |
raise Exception("Failure to raise MyException") | |
eq_(n, 3) | |
@retries(4, hook=example_exc_handler) | |
def retry_me2(): | |
raise BaseException() | |
def test_retries2(): | |
assert_raises(BaseException, retry_me2) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment