>>> def fail():
    ...     runs[0] += 1
    ...     raise ValueError()
    >>> runs = [0]; retry(sleeptime=0)(fail)()
    Traceback (most recent call last):
    ...
    ValueError
    >>> runs
    [3]
    >>> runs = [0]; retry(2, sleeptime=0)(fail)()
    Traceback (most recent call last):
    ...
    ValueError
    >>> runs
    [2]
    >>> runs = [0]; retry(exception_class=IndexError, sleeptime=0)(fail)()
    Traceback (most recent call last):
    ...
    ValueError
    >>> runs
    [1]
    >>> logger = DoctestLogger()
    >>> runs = [0]; retry(log=logger, sleeptime=0)(fail)()
    Traceback (most recent call last):
    ...
    ValueError
    >>> runs
    [3]
    >>> logger.print_logs()
    Failed with error ValueError(), trying again
    Failed with error ValueError(), trying again

def retry(num_attempts=3, exception_class=Exception, log=None, sleeptime=1):
    """
    >>> def fail():
    ...     runs[0] += 1
    ...     raise ValueError()
    >>> runs = [0]; retry(sleeptime=0)(fail)()
    Traceback (most recent call last):
    ...
    ValueError
    >>> runs
    [3]
    >>> runs = [0]; retry(2, sleeptime=0)(fail)()
    Traceback (most recent call last):
    ...
    ValueError
    >>> runs
    [2]
    >>> runs = [0]; retry(exception_class=IndexError, sleeptime=0)(fail)()
    Traceback (most recent call last):
    ...
    ValueError
    >>> runs
    [1]
    >>> logger = DoctestLogger()
    >>> runs = [0]; retry(log=logger, sleeptime=0)(fail)()
    Traceback (most recent call last):
    ...
    ValueError
    >>> runs
    [3]
    >>> logger.print_logs()
    Failed with error ValueError(), trying again
    Failed with error ValueError(), trying again
    """

    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            for i in range(num_attempts):
                try:
                    return func(*args, **kwargs)
                except exception_class as e:
                    if i == num_attempts - 1:
                        raise
                    else:
                        if log:
                            log.warn('Failed with error %r, trying again', e)
                        sleep(sleeptime)

        return wrapper

    return decorator