import logging from functools import wraps logger = logging.getLogger(__name__) class FailoverException(Exception): """ Exception raised when an operation should be retried by the failover decorator. """ def __init__(self, return_value, message): super(FailoverException, self).__init__() self.return_value = return_value self.message = message def failover(func): """ Wraps a function such that it can be retried on specified failures. Raises FailoverException when all failovers are exhausted. Example: @failover def get_google(scheme, use_www=False): www = 'www.' if use_www else '' r = requests.get(scheme + '://' + www + 'google.com') if r.status_code != 200: raise FailoverException('non 200 response from Google' ) return r def GooglePingTest(): r = get_google( (('http'), {'use_www': False}), (('http'), {'use_www': True}), (('https'), {'use_www': False}), (('https'), {'use_www': True}), ) print('Successfully contacted ' + r.url) """ @wraps(func) def wrapper(*args_sets): for arg_set in args_sets: try: return func(*arg_set[0], **arg_set[1]) except FailoverException as ex: logger.debug('failing over: %s', ex.message) return_value = ex.return_value continue return return_value return wrapper