Source code for reuse.lint

# SPDX-FileCopyrightText: 2017 Free Software Foundation Europe e.V. <https://fsfe.org>
#
# 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 .project import 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.""" bad_licenses_result = lint_bad_licenses(report, out) deprecated_result = lint_deprecated_licenses(report, out) extensionless = lint_licenses_without_extension(report, out) missing_licenses_result = lint_missing_licenses(report, out) unused_licenses_result = lint_unused_licenses(report, out) read_errors_result = lint_read_errors(report, out) files_without_cali = lint_files_without_copyright_and_licensing( report, out ) lint_summary(report, out=out) success = not any( any(result) for result in ( bad_licenses_result, deprecated_result, extensionless, missing_licenses_result, unused_licenses_result, read_errors_result, files_without_cali, ) ) 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_deprecated_licenses( report: ProjectReport, out=sys.stdout ) -> Iterable[str]: """Lint for deprecated licenses.""" deprecated = [] if report.deprecated_licenses: out.write("# ") out.write(_("DEPRECATED LICENSES")) out.write("\n\n") out.write(_("The following licenses are deprecated by SPDX:")) out.write("\n") for lic in sorted(report.deprecated_licenses): deprecated.append(lic) _write_element(lic, out=out) out.write("\n\n") return deprecated
[docs]def lint_licenses_without_extension( report: ProjectReport, out=sys.stdout ) -> Iterable[str]: """Lint for licenses without extensions.""" extensionless = [] if report.licenses_without_extension: out.write("# ") out.write(_("LICENSES WITHOUT FILE EXTENSION")) out.write("\n\n") out.write(_("The following licenses have no file extension:")) out.write("\n") for __, path in sorted(report.licenses_without_extension.items()): extensionless.append(path) _write_element(path, out=out) out.write("\n\n") return extensionless
[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_unused_licenses( report: ProjectReport, out=sys.stdout ) -> Iterable[str]: """Lint for unused licenses.""" unused_licenses = [] if report.unused_licenses: out.write("# ") out.write(_("UNUSED LICENSES")) out.write("\n\n") out.write(_("The following licenses are not used:")) out.write("\n") for lic in sorted(report.unused_licenses): unused_licenses.append(lic) _write_element(lic, out=out) out.write("\n\n") return unused_licenses
[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.""" # pylint: disable=too-many-statements 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(_("Deprecated licenses:")) for i, lic in enumerate(sorted(report.deprecated_licenses)): if i: out.write(",") out.write(" ") out.write(lic) out.write("\n") out.write("* ") out.write(_("Licenses without file extension:")) for i, lic in enumerate(sorted(report.licenses_without_extension)): 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): # pylint: disable=unused-argument """Add arguments to parser."""
[docs]def run(args, project: Project, out=sys.stdout): """List all non-compliant files.""" # pylint: disable=unused-argument report = ProjectReport.generate( project, do_checksum=False, multiprocessing=not args.no_multiprocessing ) result = lint(report, out=out) return 0 if result else 1