docs: def get_download_url(self, duration=None, preauthenticated=False, filename=None, project=None, **kwargs):
          """
          :param duration: number of seconds for which the generated URL will be
              valid, should only be specified when preauthenticated is True
          :type duration: int
          :param preauthenticated: if True, generates a 'preauthenticated'
              download URL, which embeds authentication info in the URL and does
              not require additional headers
          :type preauthenticated: bool
          :param filename: desired filename of the downloaded file
          :type filename: str
          :param project: ID of a project containing the file (the download URL
              will be associated with this project, and this may affect which
              billing account is billed for this download).
              If no project is specified, an attempt will be made to verify if the file is
              in the project from the DXFile handler (as specified by the user or
              the current project stored in dxpy.WORKSPACE_ID). Otherwise, no hint is supplied.
              This fall back behavior does not happen inside a job environment.
              A non preauthenticated URL is only valid as long as the user has
              access to that project and the project contains that file.
          :type project: str
          :returns: download URL and dict containing HTTP headers to be supplied
              with the request
          :rtype: tuple (str, dict)
          :raises: :exc:`~dxpy.exceptions.ResourceNotFound` if a project context was
              given and the file was not found in that project context.
          :raises: :exc:`~dxpy.exceptions.ResourceNotFound` if no project context was
              given and the file was not found in any projects.
  
          Obtains a URL that can be used to directly download the associated
          file.
  
          """
          args = {"preauthenticated": preauthenticated}
  
          if duration is not None:
              args["duration"] = duration
          if filename is not None:
              args["filename"] = filename
  
          # If project=None, we fall back to the project attached to this handler
          # (if any). If this is supplied, it's treated as a hint: if it's a
          # project in which this file exists, it's passed on to the
          # apiserver. Otherwise, NO hint is supplied. In principle supplying a
          # project in the handler that doesn't contain this file ought to be an
          # error, but it's this way for backwards compatibility. We don't know
          # who might be doing downloads and creating handlers without being
          # careful that the project encoded in the handler contains the file
          # being downloaded. They may now rely on such behavior.
          if project is None and 'DX_JOB_ID' not in os.environ:
              project_from_handler = self.get_proj_id()
              if project_from_handler and object_exists_in_project(self.get_id(), project_from_handler):
                  project = project_from_handler
  
          if project is not None and project is not DXFile.NO_PROJECT_HINT:
              args["project"] = project
  
          # Test hook to write 'project' argument passed to API call to a
          # local file
          if '_DX_DUMP_BILLED_PROJECT' in os.environ:
              with open(os.environ['_DX_DUMP_BILLED_PROJECT'], "w") as fd:
                  if project is not None and project != DXFile.NO_PROJECT_HINT:
                      fd.write(project)
  
          with self._url_download_mutex:
              if self._download_url is None or self._download_url_expires < time.time():
                  # The idea here is to cache a download URL for the entire file, that will
                  # be good for a few minutes. This avoids each thread having to ask the
                  # server for a URL, increasing server load.
                  #
                  # To avoid thread race conditions, this check/update procedure is protected
                  # with a lock.
  
                  # logging.debug("Download URL unset or expired, requesting a new one")
                  if "timeout" not in kwargs:
                      kwargs["timeout"] = FILE_REQUEST_TIMEOUT
                  resp = dxpy.api.file_download(self._dxid, args, **kwargs)
                  self._download_url = resp["url"]
                  self._download_url_headers = _validate_headers(resp.get("headers", {}))
                  if preauthenticated:
                      self._download_url_expires = resp["expires"]/1000 - 60  # Try to account for drift
                  else:
                      self._download_url_expires = 32503680000  # doesn't expire (year 3000)
  
              # Make a copy, ensuring each thread has its own mutable
              # version of the headers.  Note: python strings are
              # immutable, so we can safely give a reference to the
              # download url.
              retval_download_url = self._download_url
              retval_download_url_headers = copy.copy(self._download_url_headers)
  
          return retval_download_url, retval_download_url_headers