Source code for reuse.download

# SPDX-FileCopyrightText: 2019 Free Software Foundation Europe e.V. <https://fsfe.org>
#
# SPDX-License-Identifier: GPL-3.0-or-later

"""Functions for downloading license files from spdx/license-data-list."""

import errno
import sys
from gettext import gettext as _
from os import PathLike
from pathlib import Path
from urllib.parse import urljoin

import requests

from ._licenses import ALL_NON_DEPRECATED_MAP
from ._util import (
    PathType,
    find_licenses_directory,
    print_incorrect_spdx_identifier,
)
from .project import Project
from .report import ProjectReport

# All raw text files are available as files underneath this path.
_SPDX_REPOSITORY_BASE_URL = (
    "https://raw.githubusercontent.com/spdx/license-list-data/master/text/"
)


[docs]def download_license(spdx_identifier: str) -> str: """Download the license text from the SPDX repository. :param spdx_identifier: SPDX identifier of the license. :raises requests.RequestException: if the license could not be downloaded. :return: The license text. """ # This is fairly naive, but I can't see anything wrong with it. url = urljoin( _SPDX_REPOSITORY_BASE_URL, "".join((spdx_identifier, ".txt")) ) # TODO: Cache result? response = requests.get(url) if response.status_code == 200: return response.text raise requests.RequestException("Status code was not 200")
def _path_to_license_file(spdx_identifier: str, root: PathLike = None) -> Path: licenses_path = find_licenses_directory(root=root) return licenses_path / "".join((spdx_identifier, ".txt"))
[docs]def put_license_in_file(spdx_identifier: str, destination: PathLike) -> None: """Download a license and put it in the destination file. This function exists solely for convenience. :param spdx_identifier: SPDX License Identifier of the license. :param destination: Where to put the license. :raises requests.RequestException: if the license could not be downloaded. :raises FileExistsError: if the license file already exists. """ header = "" destination = Path(destination) destination.parent.mkdir(exist_ok=True) if destination.exists(): raise FileExistsError(errno.EEXIST, "File exists", str(destination)) text = download_license(spdx_identifier) with destination.open("w") as fp: fp.write(header) fp.write(text)
[docs]def add_arguments(parser) -> None: """Add arguments to parser.""" parser.add_argument( "license", action="store", nargs="*", help=_("SPDX License Identifier of license"), ) parser.add_argument( "--all", action="store_true", help=_("download all missing licenses detected in the project"), ) parser.add_argument( "--output", "-o", dest="file", action="store", type=PathType("w") )
[docs]def run(args, project: Project, out=sys.stdout) -> int: """Download license and place it in the LICENSES/ directory.""" def _already_exists(path: PathLike): out.write( _("Error: {spdx_identifier} already exists.").format( spdx_identifier=path ) ) out.write("\n") def _could_not_download(identifier: str): out.write(_("Error: Failed to download license.")) out.write(" ") if identifier not in ALL_NON_DEPRECATED_MAP: print_incorrect_spdx_identifier(identifier, out=out) else: out.write(_("Is your internet connection working?")) out.write("\n") def _successfully_downloaded(destination: PathLike): out.write( _("Successfully downloaded {spdx_identifier}.").format( spdx_identifier=destination ) ) out.write("\n") if args.all: # TODO: This is fairly inefficient, but gets the job done. report = ProjectReport.generate(project) licenses = report.missing_licenses elif not args.license: args.parser.error(_("the following arguments are required: license")) elif len(args.license) > 1 and args.file: args.parser.error(_("cannot use --output with more than one license")) else: licenses = args.license return_code = 0 for lic in licenses: if args.file: destination = args.file else: destination = _path_to_license_file(lic) try: put_license_in_file(lic, destination=destination) except requests.RequestException: _could_not_download(lic) return_code = 1 except FileExistsError as err: _already_exists(err.filename) return_code = 1 else: _successfully_downloaded(destination) return return_code