|
|
|
|
|
import functools |
|
import logging |
|
import sys |
|
import traceback |
|
import warnings |
|
|
|
|
|
|
|
_logger = logging.getLogger('backoff') |
|
_logger.addHandler(logging.NullHandler()) |
|
_logger.setLevel(logging.INFO) |
|
|
|
|
|
|
|
def _maybe_call(f, *args, **kwargs): |
|
return f(*args, **kwargs) if callable(f) else f |
|
|
|
|
|
def _init_wait_gen(wait_gen, wait_gen_kwargs): |
|
kwargs = {k: _maybe_call(v) for k, v in wait_gen_kwargs.items()} |
|
return wait_gen(**kwargs) |
|
|
|
|
|
def _next_wait(wait, jitter, elapsed, max_time): |
|
value = next(wait) |
|
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() |
|
|
|
|
|
if max_time is not None: |
|
seconds = min(seconds, max_time - elapsed) |
|
|
|
return seconds |
|
|
|
|
|
|
|
|
|
def _config_handlers(user_handlers, default_handler=None, logger=None): |
|
handlers = [] |
|
if logger is not None: |
|
|
|
log_handler = functools.partial(default_handler, logger=logger) |
|
handlers.append(log_handler) |
|
|
|
if user_handlers is None: |
|
return handlers |
|
|
|
|
|
|
|
if hasattr(user_handlers, '__iter__'): |
|
|
|
handlers += list(user_handlers) |
|
else: |
|
|
|
handlers.append(user_handlers) |
|
|
|
return handlers |
|
|
|
|
|
|
|
def _log_backoff(details, logger): |
|
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.info(msg, *log_args) |
|
|
|
|
|
|
|
def _log_giveup(details, logger): |
|
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.error(msg, *log_args) |
|
|