# Redo - Utilities to retry Python callables
******************************************
## Introduction
Redo provides various means to add seamless ability to retry to any Python callable. Redo includes a plain function `(redo.retry)`, a decorator `(redo.retriable)`, and a context manager `(redo.retrying)` to enable you to integrate it in the best possible way for your project. As a bonus, a standalone interface is also included `("retry")`.
## Installation
For installing with pip, run following commands
> pip install redo
## How To Use
Below is the list of functions available
* retrier
* retry
* retriable
* retrying (contextmanager)
### retrier(attempts=5, sleeptime=10, max_sleeptime=300, sleepscale=1.5, jitter=1)
A generator function that sleeps between retries, handles exponential back off and jitter. The action you are retrying is meant to run after retrier yields. At each iteration, we sleep for `sleeptime + random.randint(-jitter, jitter)`. Afterwards sleeptime is multiplied by sleepscale for the next iteration.
**Arguments Detail:**
1. **attempts (int):** maximum number of times to try; defaults to 5
2. **sleeptime (float):** how many seconds to sleep between tries; defaults to 60s (one minute)
3. **max_sleeptime (float):** the longest we'll sleep, in seconds; defaults to 300s (five minutes)
4. **sleepscale (float):** how much to multiply the sleep time by each iteration; defaults to 1.5
5. **jitter (int):** random jitter to introduce to sleep time each iteration. the amount is chosen at random between `[-jitter, +jitter]` defaults to 1
**Output:**
None, a maximum of `attempts` number of times
**Example:**
>>> n = 0
>>> for _ in retrier(sleeptime=0, jitter=0):
... if n == 3:
... # We did the thing!
... break
... n += 1
>>> n
3
>>> n = 0
>>> for _ in retrier(sleeptime=0, jitter=0):
... if n == 6:
... # We did the thing!
... break
... n += 1
... else:
... print("max tries hit")
max tries hit
### retry(action, attempts=5, sleeptime=60, max_sleeptime=5 * 60, sleepscale=1.5, jitter=1, retry_exceptions=(Exception,), cleanup=None, args=(), kwargs={})
Calls an action function until it succeeds, or we give up.
**Arguments Detail:**
1. **action (callable):** the function to retry
2. **attempts (int):** maximum number of times to try; defaults to 5
3. **sleeptime (float):** how many seconds to sleep between tries; defaults to 60s (one minute)
4. **max_sleeptime (float):** the longest we'll sleep, in seconds; defaults to 300s (five minutes)
5. **sleepscale (float):** how much to multiply the sleep time by each iteration; defaults to 1.5
6. **jitter (int):** random jitter to introduce to sleep time each iteration. The amount is chosen at random between `[-jitter, +jitter]` defaults to 1
7. **retry_exceptions (tuple):** tuple of exceptions to be caught. If other exceptions are raised by `action()`, then these are immediately re-raised to the caller.
8. **cleanup (callable):** optional; called if one of `retry_exceptions` is caught. No arguments are passed to the cleanup function; if your cleanup requires arguments, consider using `functools.partial` or a `lambda` function.
9. **args (tuple):** positional arguments to call `action` with
10. **kwargs (dict):** keyword arguments to call `action` with
**Output:**
Whatever action`(*args, **kwargs)` returns
**Output:**
Whatever action(*args, **kwargs) raises. `retry_exceptions` are caught
up until the last attempt, in which case they are re-raised.
**Example:**
>>> count = 0
>>> def foo():
... global count
... count += 1
... print(count)
... if count < 3:
... raise ValueError("count is too small!")
... return "success!"
>>> retry(foo, sleeptime=0, jitter=0)
1
2
3
'success!'
### retriable(*retry_args, **retry_kwargs)
A decorator factory for `retry()`. Wrap your function in `@retriable(...)` to give it retry powers!
**Arguments Detail:**
Same as for `retry`, with the exception of `action`, `args`, and `kwargs`,
which are left to the normal function definition.
**Output:**
A function decorator
**Example:**
>>> count = 0
>>> @retriable(sleeptime=0, jitter=0)
... def foo():
... global count
... count += 1
... print(count)
... if count < 3:
... raise ValueError("count too small")
... return "success!"
>>> foo()
1
2
3
'success!'
### retrying(func, *retry_args, **retry_kwargs)
A context manager for wrapping functions with retry functionality.
**Arguments Detail:**
1. **func (callable):** the function to wrap
other arguments as per `retry`
**Output:**
A context manager that returns `retriable(func)` on `__enter__`
**Example:**
>>> count = 0
>>> def foo():
... global count
... count += 1
... print(count)
... if count < 3:
... raise ValueError("count too small")
... return "success!"
>>> with retrying(foo, sleeptime=0, jitter=0) as f:
... f()
1
2
3
'success!'