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.
Simplicity
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.
importretrace@retrace.retrydefunstable():# ...
Of course, you can change what you catch by passing on_exception
which can be any valid exception class.
importretrace@retrace.retry(on_exeption=IOError)defunstable():# ...
Portability
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.
Customisation
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.
Limiters
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)defunstable():# ...
If a callable is passed in, the number of retries can be limited easily with any custom logic.
importrandomimportretracedefrandom_limit(attempt):ifattempt>random.randint(0,10):raiseretrace.LimitReached()@retrace.retry(limit=random_limit)defunstable():# ...
Intervals
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)defunstable():# ...
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.
importtime@retrace.retry(interval=time.sleep)defunstable():# ...
Validators
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"
.
@retrace.retry(validator="EXPECTED")defunstable():# ...
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.
defvalidate_string(value):returnisinstance(value,str)@retrace.retry(validator=validate_string)defunstable():# ...
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!