code: def auto_retry(exception_t, retries=3, sleepSeconds=1, back_of_factor=1, msg='', auto_log=1, raise_on_failure=True):
      
      def wrapper(func):
          def fun_call(*args, **kwargs):
              tries = 0
              while tries < retries:
                  try:
                      return func(*args, **kwargs)
                  except exception_t as e:
                      tries += 1
                      sleep_seconds = sleepSeconds * tries * back_of_factor
                      if auto_log > 0:
                          ermsg = auto_retry_msg_frmt.format(class_name_str(e, True), e, tries, retries, sleep_seconds, func.__name__, args, kwargs, msg)
                      if tries < retries:
                          if auto_log > 1:
                              LOG.info(ermsg)
                          time.sleep(sleep_seconds)
                      else:
                          if auto_log > 0:
                              LOG.exception(ermsg)
                          if raise_on_failure:
                              raise
                          return e
          return fun_call
      return wrapper
docs: a generic auto-retry function  @wrapper - decorator
  
      :param Exception exception_t: exception (or tuple of exceptions) to auto retry
      :param int retries: max retries before it raises the Exception (defaults to 3)
      :param int_or_float sleepSeconds: base sleep seconds between retries (defaults to 1)
      :param int back_of_factor: factor to back off on each retry (defaults to 1)
      :param str msg: message to append to logs
      :param int auto_log: (0 | 1 | 2) logs (exception) failures if > 0 logs (info) retries also if > 1
      :param bool raise_on_failure: if True will re-raise exception when retries are exhausted (defaults to True)
      :returns:
          function result if successful (on first place or after retrying)
          the *last* retry Exception if not successful, check not isinstance(result , Exception) to see if it managed to succeed