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
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
it has passed and
False if it has failed and the function should be called
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!