HEX
Server: LiteSpeed
System: Linux ip-172-31-76-142.ec2.internal 4.14.158-129.185.amzn2.x86_64 #1 SMP Tue Dec 24 03:15:32 UTC 2019 x86_64
User: 69b4844ae61d4e92bf26ad98af552775 (1065)
PHP: 7.2.27
Disabled: exec,passthru,shell_exec,system,eval
Upload Files
File: //lib/python2.7/site-packages/cwlogs/retry.py
#  Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
#  Licensed under the Amazon Software License (the "License").
#  You may not use this file except in compliance with the License.
#  A copy of the License is located at
#
#  http://aws.amazon.com/asl/
#
#  or in the "license" file accompanying this file. This file is distributed
#  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
#  express or implied. See the License for the specific language governing
#  permissions and limitations under the License.

import functools
import sys
import random
import time
from threading import Event


class ExponentialBackoff(object):

    """
    Decorator which performs exponential backoff and retry of a function for up
    to a maximum of max_retries every time the function returns an exception.
    If max_retries is exceeded, raise an exception with the traceback of the
    last exception thrown.

    Customize using:
     * max_retries:
       set to the number of times to retry a failing method call
       Default: 5
     * logger:
       supply a logging.Logger object to send exception information to
       this stream
       Default: None
     * exception:
       to catch only a subset of exceptions. Use a tuple to catch
       multiple exceptions
       Default: None (catches all exceptions)
     * stderr:
       set to True to print exception information to sys.stderr.
       Default: False
     * initial_backoff:
       set the number of seconds for the first backoff.
       Default: 1s
     * rand_factor:
       randomize the backoff to a value which is between
       backoff * (1-rand_factor) and backoff * (1+rand_factor)
       Default: 0.5
     * max_backoff:
       set the number of seconds for the max backoff.
       Default: 60s
     * stop_flag:
       supply an Event object to stop the retry.
       Default: None
    """

    def __init__(self, max_retries=5, logger=None, exception=Exception,
                 stderr=False, initial_backoff=1, quiet=False,
                 rand_factor=0.5, max_backoff=60, stop_flag=None):
        self.max_retries = max_retries
        self.logger = logger
        self.exception = exception
        self.stderr = stderr
        self.quiet = quiet
        self.rand_factor = rand_factor
        self.max_backoff = max_backoff
        self.stop_flag = stop_flag

        if not self.stop_flag:
            self.stop_flag = Event()
        if initial_backoff <= 0:
            raise ValueError('initial_backoff must be larger than zero: %s' %
                             initial_backoff)
        else:
            self.initial_backoff = initial_backoff
        if self.rand_factor < 0 or self.rand_factor > 1:
            raise ValueError('rand_factor must be between 0 and 1')

    def __call__(self, f):
        @functools.wraps(f)
        def wrapper(*args, **kwargs):
            retry = 0
            while retry < self.max_retries:
                try:
                    return f(*args, **kwargs)
                except self.exception as e:
                    if self.logger:
                        self.logger.warning(e)
                if self.stop_flag.is_set():
                    break
                backoff = self._calc_backoff(retry)
                # Requested logging
                if retry < self.max_retries - 1:
                    msg = 'Method "%s" failed, backing off %s seconds, ' \
                          'and retrying' % (f.__name__, backoff)
                else:
                    msg = 'Method "%s" has failed for the last time.' \
                          % (f.__name__)
                if self.quiet:
                    pass
                else:
                    if self.logger:
                        self.logger.warning(msg)
                    if self.stderr:
                        sys.stderr.write('%s\n' % msg)
                # Sleep, except after the last failed attempt
                if retry < self.max_retries - 1:
                    self.stop_flag.wait(backoff)
                retry += 1

            # Dropped out of the while loop so we've exhausted all retries
            raise RuntimeError(
                'Method "%s" has failed after %s unsuccessful '
                'attempts.' % (f.__name__, retry))

        return wrapper

    def _calc_backoff(self, retry):
        backoff = self.initial_backoff * (2 ** retry)
        if self.rand_factor:
            rand_value = random.uniform(1 - self.rand_factor,
                                        1 + self.rand_factor)
            backoff = backoff * rand_value
        if backoff >= self.max_backoff:
            return self.max_backoff
        else:
            return backoff