Module pedantic.decorators.fn_deco_retry

Expand source code
import logging
import time
from datetime import timedelta
from functools import wraps
from logging import Logger
from typing import Callable, TypeVar, Any, ParamSpec

C = TypeVar('C', bound=Callable)
P = ParamSpec('P')
R = TypeVar('R')


def retry(
    *,
    attempts: int,
    exceptions: type[Exception] | tuple[type[Exception], ...] = Exception,
    sleep_time: timedelta = timedelta(seconds=0),
    logger: Logger = None,
) -> Callable[[C], C]:
    """
    Retries the wrapped function/method `attempts` times if the exceptions listed
    in [exceptions] are thrown.

    Parameters:
        attempts: The number of times to repeat the wrapped function/method
        exceptions: Lists of exceptions that trigger a retry attempt.
        sleep_time: The time to wait between the retry attempts.
        logger: The logger used for logging.

    Example:
        >>> @retry(attempts=3, exceptions=(ValueError, TypeError))
        ... def foo():
        ...     raise ValueError('Some error')
        >>> foo()
    """

    def decorator(func: C) -> C:
        @wraps(func)
        def wrapper(*args, **kwargs) -> Any:
            return retry_func(
                func,
                *args,
                attempts=attempts,
                exceptions=exceptions,
                sleep_time=sleep_time,
                logger=logger,
                **kwargs,
            )
        return wrapper
    return decorator


def retry_func(
    func: Callable[P, R],
    *args: P.args,
    attempts: int,
    exceptions: type[Exception] | tuple[type[Exception], ...] = Exception,
    sleep_time: timedelta = timedelta(seconds=0),
    logger: Logger = None,
    **kwargs: P.kwargs,
) -> R:
    attempt = 1

    if logger is None:
        logger = logging.getLogger()

    while attempt < attempts:
        try:
            return func(*args, **kwargs)
        except exceptions:
            logger.warning(f'Exception thrown when attempting to run {func.__name__}, '
                           f'attempt {attempt} of {attempts}')
            attempt += 1
            time.sleep(sleep_time.total_seconds())

    return func(*args, **kwargs)

Functions

def retry(*, attempts: int, exceptions: type[Exception] | tuple[type[Exception], ...] = builtins.Exception, sleep_time: datetime.timedelta = datetime.timedelta(0), logger: logging.Logger = None) ‑> Callable[[~C], ~C]

Retries the wrapped function/method attempts times if the exceptions listed in [exceptions] are thrown.

Parameters

attempts: The number of times to repeat the wrapped function/method exceptions: Lists of exceptions that trigger a retry attempt. sleep_time: The time to wait between the retry attempts. logger: The logger used for logging.

Example

>>> @retry(attempts=3, exceptions=(ValueError, TypeError))
... def foo():
...     raise ValueError('Some error')
>>> foo()
Expand source code
def retry(
    *,
    attempts: int,
    exceptions: type[Exception] | tuple[type[Exception], ...] = Exception,
    sleep_time: timedelta = timedelta(seconds=0),
    logger: Logger = None,
) -> Callable[[C], C]:
    """
    Retries the wrapped function/method `attempts` times if the exceptions listed
    in [exceptions] are thrown.

    Parameters:
        attempts: The number of times to repeat the wrapped function/method
        exceptions: Lists of exceptions that trigger a retry attempt.
        sleep_time: The time to wait between the retry attempts.
        logger: The logger used for logging.

    Example:
        >>> @retry(attempts=3, exceptions=(ValueError, TypeError))
        ... def foo():
        ...     raise ValueError('Some error')
        >>> foo()
    """

    def decorator(func: C) -> C:
        @wraps(func)
        def wrapper(*args, **kwargs) -> Any:
            return retry_func(
                func,
                *args,
                attempts=attempts,
                exceptions=exceptions,
                sleep_time=sleep_time,
                logger=logger,
                **kwargs,
            )
        return wrapper
    return decorator
def retry_func(func: Callable[~P, ~R], *args: P.args, attempts: int, exceptions: type[Exception] | tuple[type[Exception], ...] = builtins.Exception, sleep_time: datetime.timedelta = datetime.timedelta(0), logger: logging.Logger = None, **kwargs: P.kwargs) ‑> ~R
Expand source code
def retry_func(
    func: Callable[P, R],
    *args: P.args,
    attempts: int,
    exceptions: type[Exception] | tuple[type[Exception], ...] = Exception,
    sleep_time: timedelta = timedelta(seconds=0),
    logger: Logger = None,
    **kwargs: P.kwargs,
) -> R:
    attempt = 1

    if logger is None:
        logger = logging.getLogger()

    while attempt < attempts:
        try:
            return func(*args, **kwargs)
        except exceptions:
            logger.warning(f'Exception thrown when attempting to run {func.__name__}, '
                           f'attempt {attempt} of {attempts}')
            attempt += 1
            time.sleep(sleep_time.total_seconds())

    return func(*args, **kwargs)