class_docstr: Download content
  
  It allows for a uniform download interface to various supported URL
  schemes (see command help for details), re-using or asking for
  authentication details maintained by datalad.
code: def __call__(
      urls,
      *,
      dataset=None,
      path=None,
      overwrite=False,
      archive=False,
      save=True,
      message=None,
  ):
      from ..downloaders.http import HTTPDownloader
      from ..downloaders.providers import Providers
  
      ds = None
      if save or dataset:
          try:
              ds = require_dataset(
                  dataset, check_installed=True, purpose="download urls"
              )
          except NoDatasetFound:
              pass
  
      common_report = {"action": "download_url", "ds": ds}
  
      got_ds_instance = isinstance(dataset, Dataset)
      dir_is_target = not path or str(path).endswith(op.sep)
      path = str(resolve_path(path or op.curdir, ds=dataset))
      if dir_is_target:
          # resolve_path() doesn't preserve trailing separators. Add one for
          # the download() call.
          path = path + op.sep
      urls = ensure_list_from_str(urls)
  
      if not dir_is_target:
          if len(urls) > 1:
              yield get_status_dict(
                  status="error",
                  message=(
                      "When specifying multiple urls, --path should point to "
                      "a directory target (with a trailing separator). Got %r",
                      path,
                  ),
                  type="file",
                  path=path,
                  **common_report,
              )
              return
          if archive:
              # make sure the file suffix indicated by a URL is preserved
              # so that any further archive processing doesn't have to
              # employ mime type inspection in order to determine the archive
              # type
              from datalad.support.network import URL
  
              suffixes = PurePosixPath(URL(urls[0]).path).suffixes
              if not Path(path).suffixes == suffixes:
                  path += "".join(suffixes)
          # we know that we have a single URL
          # download() would be fine getting an existing directory and
          # downloading the URL underneath it, but let's enforce a trailing
          # slash here for consistency.
          if op.isdir(path):
              yield get_status_dict(
                  status="error",
                  message=(
                      "Non-directory path given (no trailing separator) "
                      "but a directory with that name (after adding archive "
                      "suffix) exists"
                  ),
                  type="file",
                  path=path,
                  **common_report,
              )
              return
  
      # TODO setup fancy ui.progressbars doing this in parallel and reporting overall progress
      # in % of urls which were already downloaded
      providers = Providers.from_config_files()
      downloaded_paths = []
      path_urls = {}
      need_datalad_remote = False
      for url in urls:
          # somewhat "ugly"
          downloader = providers.get_provider(url).get_downloader(url)
          try:
              downloaded_path = downloader.download(
                  url, path=path, overwrite=overwrite
              )
          except Exception as e:
              ce = CapturedException(e)
              yield get_status_dict(
                  status="error",
                  message=str(ce),
                  type="file",
                  path=path,
                  exception=ce,
                  **common_report,
              )
          else:
              if not need_datalad_remote and (
                  downloader.authenticator
                  or downloader.credential
                  or type(downloader) != HTTPDownloader
              ):
                  need_datalad_remote = True
              downloaded_paths.append(downloaded_path)
              path_urls[downloaded_path] = url
              yield get_status_dict(
                  status="ok",
                  type="file",
                  path=downloaded_path,
                  **common_report,
              )
  
      if downloaded_paths and save and ds is not None:
          msg = (
              message
              or """\
  [DATALAD] Download URLs
  
  URLs:
    {}""".format(
                  "\n  ".join(urls)
              )
          )
  
          for r in Save()(
              downloaded_paths,
              message=msg,
              # ATTN: Pass the original dataset argument to
              # preserve relative path handling semantics.
              dataset=dataset,
              return_type="generator",
              result_renderer="disabled",
              result_xfm=None,
              result_filter=None,
              on_failure="ignore",
          ):
              yield r
  
          ds_repo = ds.repo
          if isinstance(ds_repo, AnnexRepo):
              if need_datalad_remote:
                  from datalad.customremotes.base import (
                      ensure_datalad_remote,
                  )
  
                  ensure_datalad_remote(
                      ds_repo, autoenable=True, encryption=None
                  )
  
              if got_ds_instance:
                  # Paths in `downloaded_paths` are already relative to the
                  # dataset.
                  rpaths = dict(zip(downloaded_paths, downloaded_paths))
              else:
                  # Paths in `downloaded_paths` are already relative to the
                  # current working directory. Take these relative to the
                  # dataset for use with the AnnexRepo method calls.
                  rpaths = {}
                  for orig_path, resolved in zip(
                      downloaded_paths,
                      resolve_path(downloaded_paths, ds=dataset),
                  ):
                      rpath = path_under_rev_dataset(ds, resolved)
                      if rpath:
                          rpaths[str(rpath)] = orig_path
                      else:
                          lgr.warning(
                              "Path %s not under dataset %s",
                              orig_path,
                              ds,
                          )
              annex_paths = [
                  p
                  for p, annexed in zip(
                      rpaths,
                      ds_repo.is_under_annex(list(rpaths.keys())),
                  )
                  if annexed
              ]
              if annex_paths:
                  for path in annex_paths:
                      url = path_urls[rpaths[path]]
                      try:
                          # The file is already present. This is just to
                          # register the URL.
                          ds_repo.add_url_to_file(
                              path,
                              url,
                              # avoid batch mode for single files
                              # https://github.com/datalad/datalad/issues/2849
                              batch=len(annex_paths) > 1,
                              # bypass URL size check, we already have the file
                              options=["--relaxed"],
                          )
                      except CommandError as exc:
                          lgr.warning(
                              "Registering %s with %s failed: %s",
                              path,
                              url,
                              CapturedException(exc),
                          )
  
                  if archive:
                      for path in annex_paths:
                          yield from ds.add_archive_content(
                              path,
                              delete=True,
                              on_failure="ignore",
                              return_type="generator",
                              result_renderer="disabled",
                          )
  
file_path: datalad/datalad/local/download_url.py
imports: import os.path
  from logging import getLogger
  from datalad.core.local.save import Save
  from datalad.distribution.dataset import Dataset, EnsureDataset, datasetmethod, path_under_rev_dataset, require_dataset, resolve_path
  from datalad.downloaders.providers import Provider
  from datalad.interface.base import Interface, build_doc, eval_results
  from datalad.interface.common_opts import nosave_opt, save_message_opt
  from datalad.interface.results import get_status_dict
  from datalad.support.annexrepo import AnnexRepo
  from datalad.support.constraints import EnsureNone, EnsureStr
  from datalad.support.exceptions import CapturedException, CommandError, NoDatasetFound
  from datalad.support.param import Parameter
  from datalad.utils import Path, PurePosixPath, ensure_list_from_str
label_desc: Use image node_id 1 for calling the DownloadURL obj's underlying member method code with example usage: obj.__call__(urls) without return types