7

I am new to python. I am trying to create a retry decorator that, when applied to a function, will keep retrying until some criteria is met (for simplicity, say retry 10 times).

def retry():
    def wrapper(func):
        for i in range(0,10):
            try:
                func()
                break
            except:
               continue
    return wrapper

Now that will retry on any exception. How can I change it such that it retries on specific exceptions. e.g, I want to use it like:

@retry(ValueError, AbcError)
def myfunc():
    //do something

I want myfunc to be retried only of it throws ValueError or AbcError.

1
  • Added a break now. Thanks!. Edit: I think it was wrong logic for retry. I just need the for loop I think Commented Aug 7, 2013 at 11:17

3 Answers 3

11

You can supply a tuple of exceptions to the except .. block to catch:

from functools import wraps

def retry(*exceptions, **params):
    if not exceptions:
        exceptions = (Exception,)
    tries = params.get('tries', 10)

    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kw):
            for i in range(tries):
                try:
                    return func(*args, **kw)
                except exceptions:
                    pass
        return wrapper
    return decorator

The catch-all *exceptions parameter will always result in a tuple. I've added a tries keyword as well, so you can configure the number of retries too:

@retry(ValueError, TypeError, tries=20)
def foo():
    pass

Demo:

>>> @retry(NameError, tries=3)
... def foo():
...     print 'Futzing the foo!'
...     bar
... 
>>> foo()
Futzing the foo!
Futzing the foo!
Futzing the foo!
Sign up to request clarification or add additional context in comments.

5 Comments

Actually you can catch a "variable exception or a tuple of things you can catch", but that tends to make my head hurt if I think about it too long.
@glglgl: it needed a little more than just return wrapper to make this a proper decorator factory.
I wonder if it would be useful to even parametrize the 10 with def retry(*exceptions, **config): and for i in range(0, config.get('retries', 10)):. It was not requested by the OP, but it is maybe quite useful...
Related question - What if I want to catch and rethrow the exception if it is still failing after 10 tries?
@Raze2dust: Call raise at the end of the wrapper. It'll raise the last caught exception; you don't have to do that in the except clause.
2
from functools import wraps

class retry(object):
    def __init__(self, *exceptions):
        self.exceptions = exceptions

    def __call__(self, f):
        @wraps(f) # required to save the original context of the wrapped function
        def wrapped(*args, **kwargs):
            for i in range(0,10):
                try:
                    f(*args, **kwargs)
                except self.exceptions:
                    continue
        return wrapped

Usage:

@retry(ValueError, Exception)
def f():
    print('In f')
    raise ValueError


>>> f()
In f
In f
In f
In f
In f
In f
In f
In f
In f
In f

Comments

1

You can check the error class:

except Exception as e:
    for et in error_types: #(or args)
        if isinstance(e, et):
            continue
    raise e #re-raise

1 Comment

You can except an iterable object (e.g. tuple, list) with exceptions, so no need to iterate later.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.