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.
      Wraps the exception of the initial failure.
  """
  def __init__(self, exception):
    super(FailoverException, self).__init__()
    self.exception = exception

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 ''
        try:
          r = requests.get(scheme + '://' + www + 'google.com')
        except requests.RequestException as ex:
          raise FailoverException(ex)
        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')
        exception = ex.exception
        continue
    raise exception
  return wrapper