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 @retrace.retry def unstable(): # ...
Of course, you can change what you catch by passing
on_exception which can be any valid exception class.
import retrace @retrace.retry(on_exeption=IOError) 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
retrace.py 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.
@retrace.retry(limit=10) 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() @retrace.retry(limit=random_limit) 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.
@retrace.retry(interval=1) def unstable(): # ...
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 @retrace.retry(interval=time.sleep) 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
@retrace.retry(validator="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) @retrace.retry(validator=validate_string) 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!