code: def _retry_on_connection_error(func: Callable) -> Callable:
      
      @wraps(func)
      def call(instaloader, *args, **kwargs):
          try:
              return func(instaloader, *args, **kwargs)
          except (urllib3.exceptions.HTTPError, requests.exceptions.RequestException, ConnectionException) as err:
              error_string = "{}({}): {}".format(func.__name__, ', '.join([repr(arg) for arg in args]), err)
              if (kwargs.get('_attempt') or 1) == instaloader.context.max_connection_attempts:
                  raise ConnectionException(error_string) from None
              instaloader.context.error(error_string + " [retrying; skip with ^C]", repeat_at_end=False)
              try:
                  if kwargs.get('_attempt'):
                      kwargs['_attempt'] += 1
                  else:
                      kwargs['_attempt'] = 2
                  instaloader.context.do_sleep()
                  return call(instaloader, *args, **kwargs)
              except KeyboardInterrupt:
                  instaloader.context.error("[skipped by user]", repeat_at_end=False)
                  raise ConnectionException(error_string) from None
      return call
docs: Decorator to retry the function max_connection_attemps number of times.
  
      Herewith-decorated functions need an ``_attempt`` keyword argument.
  
      This is to decorate functions that do network requests that may fail. Note that
      :meth:`.get_json`, :meth:`.get_iphone_json`, :meth:`.graphql_query` and :meth:`.graphql_node_list` already have
      their own logic for retrying, hence functions that only use these for network access must not be decorated with this
      decorator.