@@ -0,0 +1,2558 @@
+'''%0AModule for easy retries in Python.%0A%0AAll intervals are in milliseconds%0A'''%0Aimport abc%0Aimport math%0Aimport random%0Aimport time%0A%0A%0AMILLIS_PER_SEC = 1000%0AMAX_ATTEMPTS = 10%0A%0A%0Adef make_jitterer(spread=0.01, rng=random):%0A    def jitter(interval):%0A        '''Applies some random jitter'''%0A        rand_number = rng.random()*2 - 1%0A        return interval + rand_number * interval * spread%0A%0A    def no_jitter(interval):%0A        return interval%0A%0A    if spread == 0:%0A        return no_jitter%0A    else:%0A        return jitter%0A%0A%0Aclass OperationFailedException(Exception):%0A    '''Thrown if all retries failed.%0A%0A    Will carry a list of all the caught exceptions%0A    '''%0A%0Aclass Strategy(abc.ABC):%0A%0A    def __init__(self, max_attempts=MAX_ATTEMPTS, jitter_spread=None):%0A        self.max_attempts = max_attempts%0A        self.attempt = 0%0A        self.jitter = make_jitterer(jitter_spread)%0A%0A    @abc.abstractmethod%0A    def next():%0A        pass%0A%0A    def __iter__(self):%0A        return self%0A%0A    def __next__(self):%0A        if self.attempt %3C self.max_attempts:%0A            self.attempt += 1%0A            interval = self.next()%0A            return interval + self.jitter(interval)%0A        else:%0A            raise StopIteration()%0A%0A%0Aclass Backoff(Strategy):%0A    '''Implements an exponential backoff strategy'''%0A%0A    def __init__(self,%0A                 start_interval=1000,%0A                 max_interval=60000,%0A                 *args,%0A                 **kwargs):%0A        super().__init__(*args, **kwargs)%0A        self.start_interval = start_interval%0A        self.max_interval = float(max_interval)%0A        self.next_interval = self.start_interval%0A%0A    def next(self):%0A        if self.next_interval %3C self.max_interval:%0A            self.next_interval = math.pow(2, self.attempt) * self.start_interval%0A        return min(%5Bself.next_interval, self.max_interval%5D)%0A%0A%0Aclass Linear(Strategy):%0A    def __init__(self, interval=None, *args, **kwargs):%0A        super().__init__(*args, **kwargs)%0A        self.interval_ms = interval_ms%0A%0A    def next(self):%0A        return self.interval_ms%0A%0A%0Adef retry(strategy=Linear, exception=Exception, *retry_args, **retry_kwargs):%0A    def decorate(func):%0A        def func_wrapper(*func_args, **func_kwargs):%0A            strat = strategy(*retry_args, **retry_kwargs)%0A            exceptions = %5B%5D%0A            for interval in strat:%0A                try:%0A                    func(*func_args, **func_kwargs)%0A                except exception:%0A                    continue%0A                else:%0A                    break%0A        return func_wrapper%0A    return decorate%0A

Implement basic retry with linear and backoff strategies