"""Retry decorator for transient failures.""" import time from functools import wraps from werkzeug.exceptions import HTTPException def retry_on_exception(retries=5, delay_secs=0.0, multiplier=2.0): """Call a function and retries if an exception is raised. HTTPExceptions (from abort()) are never retried — they represent a final HTTP response decision. Only transient errors (e.g. deadlocks, lock timeouts) are retried. """ def inner(fn): """Call the inner function.""" @wraps(fn) def wrapper(*args, **kwargs): """Call the wrapper function.""" nonlocal delay_secs for attempt in range(1, retries + 1): try: res = fn(*args, **kwargs) return res except HTTPException: raise except Exception as e: if attempt >= retries: raise e if delay_secs > 0.0: time.sleep(delay_secs) delay_secs *= multiplier return wrapper return inner