Source code for prereise.gather.request_util

import functools
import time
from urllib.error import HTTPError


[docs]class TransientError(Exception): """Used for errors which can be retried""" pass
[docs]class RateLimit: """Provides a way to call an arbitrary function at most once per interval. :param int/float interval: the amount of time in seconds to wait between actions """ def __init__(self, interval=None): """Constructor""" self.interval = interval self.last_run_at = None if interval is None else time.time() - interval
[docs] def invoke(self, action): """Call the action and return its value, waiting if necessary :param callable action: the thing to do :return: (*Any*) -- the return value of the action """ if self.interval is None: return action() elapsed = time.time() - self.last_run_at if elapsed < self.interval: time.sleep(self.interval - elapsed) result = action() self.last_run_at = time.time() return result
[docs]def rate_limit(_func=None, interval=None): def decorator(func): limiter = RateLimit(interval) @functools.wraps(func) def wrapper(*args, **kwargs): return limiter.invoke(lambda: func(*args, **kwargs)) return wrapper return decorator if _func is None else decorator(_func)
[docs]def retry( _func=None, max_attempts=5, interval=None, raises=False, allowed_exceptions=(HTTPError), ): """Creates a decorator to handle retry logic. :param int max_attempts: the max number of retries :param int/float interval: minimum spacing between retries :param bool raises: whether to re-raise the error after max_attempts is reached :param tuple allowed_exceptions: exceptions for which the function will be retried, all others will be surfaced to the caller :return: (*Any*) -- the return value of the decorated function, or None if raises is False and all attempts failed """ def decorator(func): limiter = RateLimit(interval) @functools.wraps(func) def wrapper(*args, **kwargs): wrapper.retry_count = 0 for i in range(max_attempts): wrapper.retry_count = i + 1 try: return limiter.invoke(lambda: func(*args, **kwargs)) except allowed_exceptions as e: if wrapper.retry_count == max_attempts: print("Max retries reached!!") if raises: raise e return wrapper return decorator if _func is None else decorator(_func)