We’ve been using the functions packaged in Redo for a few years now at Mozilla. One of the things we’ve been striving for with it is the ability to write the most natural code possible. In it’s simplest form, retry, a callable that may raise, the exceptions to retry on, and the callable to run to cleanup before another attempt – are all passed in as arguments. As a result, we have a number of code blocks like this, which don’t feel very Pythonic:
retry(self.session.request, sleeptime=5, max_sleeptime=15, retry_exceptions=(requests.HTTPError, requests.ConnectionError), attempts=self.retries, kwargs=dict(method=method, url=url, data=data, config=self.config, timeout=self.timeout, auth=self.auth, params=params) )
It’s particularly unfortunate that you’re forced to let retry do your exception handling and cleanup – I find that it makes the code a lot less readable. It’s also not possible to do anything in a finally block, unless you wrap the retry in one.
Recently, Chris AtLee discovered a new method of doing retries that results in much cleaner and more readable code. With it, the above block can be rewritten as:
for attempt in retrier(attempts=self.retries): try: self.session.request(method=method, url=url, data=data, config=self.config, timeout=self.timeout, auth=self.auth, params=params) break except (requests.HTTPError, requests.ConnectionError), e: pass
retrier simply handles the the mechanics of tracking attempts and sleeping, leaving your code to do all of its own exception handling and cleanup – just as if you weren’t retrying at all. It’s important to note that the break at the end of the try block is important, otherwise self.session.request would run even if it succeeded.
I released Redo 1.3 with this new functionality this morning – enjoy!