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