# coding:utf-8 import functools import logging import sys import traceback import warnings # Use module-specific logger with a default null handler. _logger = logging.getLogger('backoff') _logger.addHandler(logging.NullHandler()) # pragma: no cover _logger.setLevel(logging.INFO) # Evaluate arg that can be either a fixed value or a callable. def _maybe_call(f, *args, **kwargs): if callable(f): try: return f(*args, **kwargs) except TypeError: return f else: return f def _init_wait_gen(wait_gen, wait_gen_kwargs): kwargs = {k: _maybe_call(v) for k, v in wait_gen_kwargs.items()} initialized = wait_gen(**kwargs) initialized.send(None) # Initialize with an empty send return initialized def _next_wait(wait, send_value, jitter, elapsed, max_time): value = wait.send(send_value) try: if jitter is not None: seconds = jitter(value) else: seconds = value except TypeError: warnings.warn( "Nullary jitter function signature is deprecated. Use " "unary signature accepting a wait value in seconds and " "returning a jittered version of it.", DeprecationWarning, stacklevel=2, ) seconds = value + jitter() # don't sleep longer than remaining allotted max_time if max_time is not None: seconds = min(seconds, max_time - elapsed) return seconds def _prepare_logger(logger): if isinstance(logger, str): logger = logging.getLogger(logger) return logger # Configure handler list with user specified handler and optionally # with a default handler bound to the specified logger. def _config_handlers( user_handlers, *, default_handler=None, logger=None, log_level=None ): handlers = [] if logger is not None: assert log_level is not None, "Log level is not specified" # bind the specified logger to the default log handler log_handler = functools.partial( default_handler, logger=logger, log_level=log_level ) handlers.append(log_handler) if user_handlers is None: return handlers # user specified handlers can either be an iterable of handlers # or a single handler. either way append them to the list. if hasattr(user_handlers, '__iter__'): # add all handlers in the iterable handlers += list(user_handlers) else: # append a single handler handlers.append(user_handlers) return handlers # Default backoff handler def _log_backoff(details, logger, log_level): msg = "Backing off %s(...) for %.1fs (%s)" log_args = [details['target'].__name__, details['wait']] exc_typ, exc, _ = sys.exc_info() if exc is not None: exc_fmt = traceback.format_exception_only(exc_typ, exc)[-1] log_args.append(exc_fmt.rstrip("\n")) else: log_args.append(details['value']) logger.log(log_level, msg, *log_args) # Default giveup handler def _log_giveup(details, logger, log_level): msg = "Giving up %s(...) after %d tries (%s)" log_args = [details['target'].__name__, details['tries']] exc_typ, exc, _ = sys.exc_info() if exc is not None: exc_fmt = traceback.format_exception_only(exc_typ, exc)[-1] log_args.append(exc_fmt.rstrip("\n")) else: log_args.append(details['value']) logger.log(log_level, msg, *log_args)