diff --git a/src/spdx3_validate/__init__.py b/src/spdx3_validate/__init__.py index 1ea3ffa..3f08a14 100644 --- a/src/spdx3_validate/__init__.py +++ b/src/spdx3_validate/__init__.py @@ -1 +1,8 @@ -from .main import main # noqa: F401 +# SPDX-FileCopyrightText: Copyright (c) 2024 Joshua Watt +# SPDX-License-Identifier: MIT + +"""Initialization for spdx3_validate package.""" + +from .main import main + +__all__ = ["main"] diff --git a/src/spdx3_validate/__main__.py b/src/spdx3_validate/__main__.py index 9645d27..9ff6dcb 100644 --- a/src/spdx3_validate/__main__.py +++ b/src/spdx3_validate/__main__.py @@ -3,6 +3,9 @@ # Copyright (c) 2024 Joshua Watt # # SPDX-License-Identifier: MIT + +"""Main entry point for SPDX 3 validation tool.""" + from .main import main main() diff --git a/src/spdx3_validate/main.py b/src/spdx3_validate/main.py index 42ae2d3..afb1556 100644 --- a/src/spdx3_validate/main.py +++ b/src/spdx3_validate/main.py @@ -2,35 +2,41 @@ # # SPDX-License-Identifier: MIT +"""Main module for SPDX 3 validation tool.""" + import argparse -import halo import json +import urllib.request +import sys +import textwrap + +from pathlib import Path + +import halo import jsonschema import pyshacl import rdflib -import sys -import textwrap -import urllib.request from rdflib import RDF, RDFS, SH, URIRef -from pathlib import Path from .version import VERSION from .spdx_versions import find_version, SPDX_VERSIONS def read_location(location): + """Read a file from a location.""" if "://" in location: with urllib.request.urlopen(location) as f: return f.read() elif location == "-": return sys.stdin.read() else: - with Path(location).open("r") as f: + with Path(location).open("r", encoding="utf-8") as f: return f.read() def derives_from(cls, target, shacl_graph): + """Check if a class derives from another class.""" if cls == target: return True @@ -42,6 +48,7 @@ def derives_from(cls, target, shacl_graph): def check_graph(graph, shacl_graph, current_version, error_external): + """Check a graph against SHACL shapes.""" errors = [] conforms, results, _ = pyshacl.validate( @@ -77,11 +84,6 @@ def pnode(n): external_spdxids.add(str(spdxid)) def check_external_ref_error(r): - nonlocal results - nonlocal shacl_graph - nonlocal graph - nonlocal external_spdxids - if (r, RDF.type, SH.ValidationResult) not in results: return False @@ -147,6 +149,7 @@ def check_external_ref_error(r): def iter_validation_errors(err): + """Iterate over all validation errors.""" if err.context: for e in err.context: yield e @@ -154,6 +157,8 @@ def iter_validation_errors(err): def print_schema_error(err, filename, indent=0): + """Print a JSON Schema validation error.""" + def print_err(e, indent, fn=None, message=False): loc = e.json_path if fn: @@ -194,6 +199,7 @@ def print_err(e, indent, fn=None, message=False): def main(cmdline_args=None): + """Main entry point for SPDX 3 validation tool.""" parser = argparse.ArgumentParser( description=f"Validate SPDX 3 files Version {VERSION}" ) @@ -264,7 +270,8 @@ def main(cmdline_args=None): elif current_version != version: spinner.fail() print( - f"{j} has incompatible version {version.pretty}. Other documents are {current_version.pretty}" + f"{j} has incompatible version {version.pretty}. " + f"Other documents are {current_version.pretty}" ) return 1 @@ -311,20 +318,20 @@ def main(cmdline_args=None): if json_errors: print(f"ERROR: JSON Schema validation failed for {fn}:") - for e in json_errors: - print_schema_error(e, fn) + for err in json_errors: + print_schema_error(err, fn) errors += 1 with halo.Halo(f"Checking SHACL for {fn}", enabled=not args.quiet) as spinner: - e = check_graph(g, shacl_graph, current_version, True) - if e: + err = check_graph(g, shacl_graph, current_version, True) + if err: spinner.fail() else: spinner.succeed() - if e: + if err: print(f"ERROR: SHACL Validation failed for {fn}:") - print("\n".join(e)) + print("\n".join(err)) errors += 1 if len(files) > 1 and args.check_merged: @@ -334,15 +341,15 @@ def main(cmdline_args=None): for _, _, g in files: merged_g += g - e = check_graph(g, shacl_graph, current_version, False) - if e: + err = check_graph(g, shacl_graph, current_version, False) + if err: spinner.fail() else: spinner.succeed() - if e: + if err: print("ERROR: SHACL Validation failed on merged files:") - print("\n".join(e)) + print("\n".join(err)) errors += 1 else: print( diff --git a/src/spdx3_validate/spdx_versions.py b/src/spdx3_validate/spdx_versions.py index 1e56c26..4be1dad 100644 --- a/src/spdx3_validate/spdx_versions.py +++ b/src/spdx3_validate/spdx_versions.py @@ -2,11 +2,12 @@ # # SPDX-License-Identifier: MIT +"""SPDX versions and related utilities.""" + from collections import namedtuple from rdflib import RDF, URIRef - SpdxVersion = namedtuple( "SpdxVersion", ["context_url", "shacl_url", "schema_url", "pretty", "rdf_base", "get_imports"], @@ -14,21 +15,25 @@ def get_3_0_0_imports(graph): - RDF_BASE = URIRef("https://spdx.org/rdf/3.0.0/terms/") + """Get imported SPDX IDs from an SPDX 3.0.0 graph.""" + RDF_BASE = URIRef( + "https://spdx.org/rdf/3.0.0/terms/" + ) # pylint: disable=invalid-name for doc in graph.subjects(RDF.type, RDF_BASE + "Core/SpdxDocument"): for i in graph.objects(doc, RDF_BASE + "Core/imports"): - for spdxid in graph.objects(i, RDF_BASE + "Core/externalSpdxId"): - yield spdxid + yield from graph.objects(i, RDF_BASE + "Core/externalSpdxId") def get_3_0_1_imports(graph): - RDF_BASE = URIRef("https://spdx.org/rdf/3.0.1/terms/") + """Get imported SPDX IDs from an SPDX 3.0.1 graph.""" + RDF_BASE = URIRef( + "https://spdx.org/rdf/3.0.1/terms/" + ) # pylint: disable=invalid-name for doc in graph.subjects(RDF.type, RDF_BASE + "Core/SpdxDocument"): for i in graph.objects(doc, RDF_BASE + "Core/import"): - for spdxid in graph.objects(i, RDF_BASE + "Core/externalSpdxId"): - yield spdxid + yield from graph.objects(i, RDF_BASE + "Core/externalSpdxId") SPDX_VERSIONS = ( @@ -52,6 +57,7 @@ def get_3_0_1_imports(graph): def find_version(context_url): + """Find an SPDX version by its context URL.""" for s in SPDX_VERSIONS: if s.context_url == context_url: return s diff --git a/src/spdx3_validate/version.py b/src/spdx3_validate/version.py index 0862e1e..b67b871 100644 --- a/src/spdx3_validate/version.py +++ b/src/spdx3_validate/version.py @@ -1 +1,6 @@ +# SPDX-FileCopyrightText: Copyright (c) 2024 Joshua Watt +# SPDX-License-Identifier: MIT + +"""Library version information.""" + VERSION = "0.0.5"