Source code for reuse.lint

# SPDX-FileCopyrightText: 2017-2019 Free Software Foundation Europe e.V.
#
# SPDX-License-Identifier: GPL-3.0-or-later

"""All linting happens here. The linting here is nothing more than reading
the reports and printing some conclusions.
"""

import sys
from gettext import gettext as _
from typing import Iterable

from . import __REUSE_version__
from ._util import PathType
from .project import create_project
from .report import ProjectReport


def _write_element(element, out=sys.stdout):
    out.write("* ")
    out.write(str(element))
    out.write("\n")


[docs]def lint(report: ProjectReport, out=sys.stdout) -> bool: """Lint the entire project.""" files_without_cali = lint_files_without_copyright_and_licensing( report, out ) bad_licenses_result = lint_bad_licenses(report, out) missing_licenses_result = lint_missing_licenses(report, out) read_errors_result = lint_read_errors(report, out) lint_summary(report, out=out) success = not any( any(result) for result in ( files_without_cali, bad_licenses_result, missing_licenses_result, read_errors_result, # TODO: Should this be a separate entry if it's already in the # summary? report.unused_licenses, ) ) out.write("\n") if success: out.write( _( "Congratulations! Your project is compliant with version " "{} of the REUSE Specification :-)" ).format(__REUSE_version__) ) else: out.write( _( "Unfortunately, your project is not compliant with version " "{} of the REUSE Specification :-(" ).format(__REUSE_version__) ) out.write("\n") return success
[docs]def lint_bad_licenses(report: ProjectReport, out=sys.stdout) -> Iterable[str]: """Lint for bad licenses. Bad licenses are licenses that are not in the SPDX License List or do not start with LicenseRef-. """ bad_files = [] if report.bad_licenses: out.write("# ") out.write(_("BAD LICENSES")) out.write("\n") for lic, files in sorted(report.bad_licenses.items()): out.write("\n") out.write(_("'{}' found in:").format(lic)) out.write("\n") for file_ in sorted(files): bad_files.append(file_) _write_element(file_, out=out) out.write("\n\n") return bad_files
[docs]def lint_missing_licenses( report: ProjectReport, out=sys.stdout ) -> Iterable[str]: """Lint for missing licenses. A license is missing when it is referenced in a file, but cannot be found. """ bad_files = [] if report.missing_licenses: out.write("# ") out.write(_("MISSING LICENSES")) out.write("\n") for lic, files in sorted(report.missing_licenses.items()): out.write("\n") out.write(_("'{}' found in:").format(lic)) out.write("\n") for file_ in sorted(files): bad_files.append(file_) _write_element(file_, out=out) out.write("\n\n") return bad_files
[docs]def lint_read_errors(report: ProjectReport, out=sys.stdout) -> Iterable[str]: """Lint for read errors.""" bad_files = [] if report.read_errors: out.write("# ") out.write(_("READ ERRORS")) out.write("\n\n") out.write(_("Could not read:")) out.write("\n") for file_ in report.read_errors: bad_files.append(file_) _write_element(file_, out=out) out.write("\n\n") return bad_files
[docs]def lint_summary(report: ProjectReport, out=sys.stdout) -> None: """Print a summary for linting.""" out.write("# ") out.write(_("SUMMARY")) out.write("\n\n") file_total = len(report.file_reports) out.write("* ") out.write(_("Bad licenses:")) for i, lic in enumerate(sorted(report.bad_licenses)): if i: out.write(",") out.write(" ") out.write(lic) out.write("\n") out.write("* ") out.write(_("Missing licenses:")) for i, lic in enumerate(sorted(report.missing_licenses)): if i: out.write(",") out.write(" ") out.write(lic) out.write("\n") out.write("* ") out.write(_("Unused licenses:")) for i, lic in enumerate(sorted(report.unused_licenses)): if i: out.write(",") out.write(" ") out.write(lic) out.write("\n") out.write("* ") out.write(_("Used licenses:")) for i, lic in enumerate(sorted(report.used_licenses)): if i: out.write(",") out.write(" ") out.write(lic) out.write("\n") out.write("* ") out.write(_("Read errors: {count}".format(count=len(report.read_errors)))) out.write("\n") out.write("* ") out.write( _( "Files with copyright information: {count} / {total}".format( count=file_total - len(report.files_without_copyright), total=file_total, ) ) ) out.write("\n") out.write("* ") out.write( _( "Files with license information: {count} / {total}".format( count=file_total - len(report.files_without_licenses), total=file_total, ) ) ) out.write("\n")
[docs]def add_arguments(parser): """Add arguments to parser.""" parser.add_argument("path", action="store", nargs="*", type=PathType("r"))
[docs]def run(args, out=sys.stdout): """List all non-compliant files.""" project = create_project() paths = args.path if not paths: paths = [project.root] report = ProjectReport.generate(project, paths, do_checksum=False) result = lint(report, out=out) return 0 if result else 1