Retrace is, essentially, a retry decorator. There are many projects like this but after trying a few I either ran into issues or found the ...'>

Sep 12 2016

Retrace - Configurable, elegant retrying

published by Dougal Matthews
in blog Dougal Matthews
original entryRetrace - Configurable, elegant retrying

exception python retry

After I mentioned Retrace here recently a few people have asked me about it, so I thought I'd write up a quick post.

Retrace is, essentially, a retry decorator. There are many projects like this but after trying a few I either ran into issues or found the API cumbersome. I wanted something with a really simple interface but with the ability to easily build on it.


By default, it will retry the function on any exception which subclasses Exception. Any exceptions that directly inherit from BaseException (like KeyboardInterrupt) wont be caught by default, as you generally don't want that.

import retrace

def unstable():
    # ...

Of course, you can change what you catch by passing on_exception which can be any valid exception class.

import retrace

def unstable():
    # ...


Retrace is tested and supported on Python 2.7, 3.3, 3.4 and 3.5. It is also designed to be easily vendorable, I understand that you might not want, or be able to include a dependency for such a small utility. so you can easily just grab the file and include it in your project.


Retrace supports limiters, intervals and validators. These are all fairly similar concepts, but play a different role. We will quickly take a look at each of these.


A limiter defines how many times the function should be retried. This can be passed in as either a int or a callable.

For example, retry a maximum of 10 times.

def unstable():
    # ...

If a callable is passed in, the number of retries can be limited easily with any custom logic.

import random
import retrace

def random_limit(attempt):
    if attempt > random.randint(0, 10):
        raise retrace.LimitReached()

def unstable():
    # ...


Intervals define the delay that is introduced between attempts. This can either be passed in as an int (which will then be the number of seconds) or as a callable.

Delay for 1 second between attempts.

def unstable():
    # ...

Delay for n seconds, where n is the current number of attempts. This then gradually increases the delay by one second each try.

This works because time.sleep is a callable and we pass in the current attempt number each time.

import time

def unstable():
    # ...


Validators are used to verify that the result from the function passes a check.

If it isn't a callable, it can be any object that is then compared with the result. Check that the function returns the value "EXPECTED".

def unstable():
    # ...

If it is a callable, it will be passed the result and it should return True it has passed and False if it has failed and the function should be called again.

def validate_string(value):
    return isinstance(value, str)

def unstable():
    # ...

It's a small project, but I find it useful fairly frequently. If this is something that interests you I would really like your feedback. How could it be made better? What do you need that I have not through of? Please send me your ideas!

Tag cloud

Social Sharing