def retry_on_exception(tries=<NUM_LIT:6>, delay=<NUM_LIT:1>, backoff=<NUM_LIT:2>, max_delay=<NUM_LIT:32>):
tries = math.floor(tries)<EOL>if tries < <NUM_LIT:1>:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>if delay < <NUM_LIT:0>:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>if backoff < <NUM_LIT:1>:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>if max_delay < delay:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>def decorated_function_with_retry(func):<EOL><INDENT>@wraps(func)<EOL>def function_to_retry(*args, **kwargs):<EOL><INDENT>local_tries, local_delay = tries, delay<EOL>while local_tries > <NUM_LIT:1>:<EOL><INDENT>try:<EOL><INDENT>return func(*args, **kwargs)<EOL><DEDENT>except Exception as e:<EOL><INDENT>if local_delay > max_delay:<EOL><INDENT>local_delay = max_delay<EOL><DEDENT>logging.exception('<STR_LIT>'<EOL>% (str(e), local_delay))<EOL>time.sleep(local_delay)<EOL>local_tries -= <NUM_LIT:1><EOL>local_delay *= backoff<EOL><DEDENT><DEDENT>return func(*args, **kwargs)<EOL><DEDENT>return function_to_retry<EOL><DEDENT>return decorated_function_with_retry<EOL>
Decorator for implementing exponential backoff for retrying on failures.

tries: Max number of tries to execute the wrapped function before failing.
delay: Delay time in seconds before the FIRST retry.
backoff: Multiplier to extend the initial delay by for each retry.
max_delay: Max time in seconds to wait between retries.