class_docstr: Base class for the downloaders
class_docstr_tok: ["Base","class","for","the","downloaders"]
docstr: Generic decorator to manage access to the URL via some method
  
  Parameters
  ----------
  method : callable
    A callable, usually a method of the same class, which we decorate
    with access handling, and pass url as the first argument
  url : string
    URL to access
  *args, **kwargs
    Passed into the method call
  
  Returns
  -------
  None or bytes
docstr_tok: ["Generic","decorator","to","manage","access","to","the","URL","via","some","method","Parameters","--","--","--","--","--","method",":","callable","A","callable",",","usually","a","method","of","the","same","class",",","which","we","decorate","with","access","handling",",","and","pass","url","as","the","first","argument","url",":","string","URL","to","access","*","args",",","*","*","kwargs","Passed","into","the","method","call","Returns","--","--","--","-","None","or","bytes"]
code: def access(self, method, url, allow_old_session=True, **kwargs):
      """Generic decorator to manage access to the URL via some method
  
      Parameters
      ----------
      method : callable
        A callable, usually a method of the same class, which we decorate
        with access handling, and pass url as the first argument
      url : string
        URL to access
      *args, **kwargs
        Passed into the method call
  
      Returns
      -------
      None or bytes
      """
      # TODO: possibly wrap this logic outside within a decorator, which
      # would just call the corresponding method
      authenticator = self.authenticator
      if authenticator:
          needs_authentication = authenticator.requires_authentication
      else:
          needs_authentication = self.credential
  
      # TODO: not sure yet, where is/are the right spot(s) to pass the URL:
      if hasattr(self.credential, "set_context"):
          lgr.debug("set credential context as %s", url)
          self.credential.set_context(auth_url=url)
  
      attempt, incomplete_attempt = 0, 0
      result = None
      credential_was_refreshed = False
      while True:
          attempt += 1
          if attempt > 20:
              # are we stuck in a loop somehow? I think logic doesn't allow this atm
              raise RuntimeError(
                  "Got to the %d'th iteration while trying to download %s"
                  % (attempt, url)
              )
          exc_info = None
          msg_types = ""
          supported_auth_types = []
          used_old_session = False
          # Lock must be instantiated here, within each thread to avoid problems
          # when used in our parallel.ProducerConsumer
          # see https://github.com/datalad/datalad/issues/6483
          interp_lock = InterProcessLock(
              op.join(
                  cfg.obtain("datalad.locations.locks"),
                  "downloader-auth.lck",
              )
          )
  
          try:
              # Try to lock since it might desire to ask for credentials, but still allow to time out at 5 minutes
              # while providing informative message on what other process might be holding it.
              with try_lock_informatively(
                  interp_lock,
                  purpose="establish download session",
                  proceed_unlocked=False,
              ):
                  used_old_session = self._establish_session(
                      url, allow_old=allow_old_session
                  )
              if not allow_old_session:
                  assert not used_old_session
              lgr.log(5, "Calling out into %s for %s", method, url)
              result = method(url, **kwargs)
              # assume success if no puke etc
              break
          except AccessDeniedError as e:
              ce = CapturedException(e)
              if hasattr(e, "status") and e.status == 429:
                  # Too many requests.
                  # We can retry by continuing the loop.
                  time.sleep(0.5 * (attempt**1.2))
                  continue
  
              if isinstance(e, AnonymousAccessDeniedError):
                  access_denied = "Anonymous access"
              else:
                  access_denied = "Access"
              lgr.debug("%s was denied: %s", access_denied, ce)
              supported_auth_types = e.supported_types
              exc_info = sys.exc_info()
  
              if supported_auth_types:
                  msg_types = (
                      " The failure response indicated that following "
                      "authentication types should be used: %s"
                      % (", ".join(supported_auth_types))
                  )
              # keep inside except https://github.com/datalad/datalad/issues/3621
              # TODO: what if it was anonimous attempt without authentication,
              #     so it is not "requires_authentication" but rather
              #     "supports_authentication"?  We should not report below in
              # _get_new_credential that authentication has failed then since there
              # were no authentication.  We might need a custom exception to
              # be caught above about that
  
              allow_old_session = False  # we will either raise or auth
              # in case of parallel downloaders, one would succeed to get the
              # lock, ask user if necessary and other processes would just wait
              # got it to return back
              with try_lock(interp_lock) as got_lock:
                  if got_lock:
                      if (
                          isinstance(e, AccessPermissionExpiredError)
                          and not credential_was_refreshed
                          and self.credential
                          and isinstance(
                              self.credential, CompositeCredential
                          )
                      ):
                          lgr.debug(
                              "Requesting refresh of the credential (once)"
                          )
                          self.credential.refresh()
                          # to avoid a loop of refreshes without giving a chance to
                          # enter a new one, we will allow only a single refresh
                          credential_was_refreshed = True
                      else:
                          self._handle_authentication(
                              url,
                              needs_authentication,
                              e,
                              ce,
                              access_denied,
                              msg_types,
                              supported_auth_types,
                              used_old_session,
                          )
                  else:
                      lgr.debug(
                          "The lock for downloader authentication was not available."
                      )
                      # We will just wait for the lock to become available,
                      # and redo connect/download attempt
              continue
  
          except IncompleteDownloadError as e:
              ce = CapturedException(e)
              exc_info = sys.exc_info()
              incomplete_attempt += 1
              if incomplete_attempt > 5:
                  # give up
                  raise
              lgr.debug(
                  "Failed to download fully, will try again: %s", ce
              )
              # TODO: may be fail earlier than after 20 attempts in such a case?
          except DownloadError:
              # TODO Handle some known ones, possibly allow for a few retries, otherwise just let it go!
              raise
  
      return result
  
imports: import msgpack
  import os
  import sys
  import time
  from abc import ABCMeta, abstractmethod
  import os.path
  from os.path import exists, join, isdir
  from ..None import cfg
  from ..ui import ui
  from ..utils import auto_repr, ensure_unicode, unlink
  from .credentials import CompositeCredential
  from datalad.downloaders import CREDENTIAL_TYPES
  from ..support.exceptions import AccessDeniedError, AccessPermissionExpiredError, AnonymousAccessDeniedError, CapturedException, DownloadError, IncompleteDownloadError, UnaccountedDownloadError
  from ..support.locking import InterProcessLock, try_lock, try_lock_informatively
  from logging import getLogger
label_desc: Use image node_id 2 for calling the BaseDownloader obj's underlying member method code with example usage: obj.access(method, url, allow_old_session) and returns: result