code: async def retry_async(func, attempts=5, sleeptime_callback=calculate_sleep_time,
                        retry_exceptions=Exception, args=(), kwargs=None,
                        sleeptime_kwargs=None):
      
      kwargs = kwargs or {}
      attempt = 1
      while True:
          try:
              return await func(*args, **kwargs)
          except retry_exceptions:
              attempt += 1
              if attempt > attempts:
                  log.warning("retry_async: {}: too many retries!".format(func.__name__))
                  raise
              sleeptime_kwargs = sleeptime_kwargs or {}
              sleep_time = sleeptime_callback(attempt, **sleeptime_kwargs)
              log.debug("retry_async: {}: sleeping {} seconds before retry".format(func.__name__, sleep_time))
              await asyncio.sleep(sleep_time)
docs: Retry ``func``, where ``func`` is an awaitable.
  
      Args:
          func (function): an awaitable function.
          attempts (int, optional): the number of attempts to make.  Default is 5.
          sleeptime_callback (function, optional): the function to use to determine
              how long to sleep after each attempt.  Defaults to ``calculateSleepTime``.
          retry_exceptions (list or exception, optional): the exception(s) to retry on.
              Defaults to ``Exception``.
          args (list, optional): the args to pass to ``function``.  Defaults to ()
          kwargs (dict, optional): the kwargs to pass to ``function``.  Defaults to
              {}.
          sleeptime_kwargs (dict, optional): the kwargs to pass to ``sleeptime_callback``.
              If None, use {}.  Defaults to None.
  
      Returns:
          object: the value from a successful ``function`` call
  
      Raises:
          Exception: the exception from a failed ``function`` call, either outside
              of the retry_exceptions, or one of those if we pass the max
              ``attempts``.