spdx_tools.spdx.clitools.pyspdxtools

  1#!/usr/bin/env python3
  2
  3# Copyright (c) 2020 Yash Varshney
  4# Copyright (c) 2023 spdx contributors
  5# SPDX-License-Identifier: Apache-2.0
  6# Licensed under the Apache License, Version 2.0 (the "License");
  7# you may not use this file except in compliance with the License.
  8# You may obtain a copy of the License at
  9#    http://www.apache.org/licenses/LICENSE-2.0
 10# Unless required by applicable law or agreed to in writing, software
 11# distributed under the License is distributed on an "AS IS" BASIS,
 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13# See the License for the specific language governing permissions and
 14# limitations under the License.
 15import logging
 16import sys
 17from json import JSONDecodeError
 18from xml.parsers.expat import ExpatError
 19from xml.sax import SAXParseException
 20
 21import click
 22from beartype.typing import List
 23from yaml.scanner import ScannerError
 24
 25from spdx_tools.spdx.graph_generation import export_graph_from_document
 26from spdx_tools.spdx.model import Document
 27from spdx_tools.spdx.parser.error import SPDXParsingError
 28from spdx_tools.spdx.parser.parse_anything import parse_file
 29from spdx_tools.spdx.validation.document_validator import validate_full_spdx_document
 30from spdx_tools.spdx.validation.validation_message import ValidationMessage
 31from spdx_tools.spdx.writer.tagvalue import tagvalue_writer
 32from spdx_tools.spdx.writer.write_anything import write_file
 33
 34
 35@click.command()
 36@click.option("--infile", "-i", required=True, help="The file containing the document to be validated or converted.")
 37@click.option(
 38    "--outfile",
 39    "-o",
 40    help="The file to write the converted document to (write a dash for output to stdout or omit for no conversion). "
 41    "If you add the option --graph to the command the generated graph will be written to this file.",
 42)
 43@click.option(
 44    "--version",
 45    help='The SPDX version to be used during parsing and validation ("SPDX-2.2" or "SPDX-2.3"). '
 46    "Will be read from the document if not provided.",
 47    default=None,
 48)
 49@click.option("--novalidation", is_flag=True, help="Don't validate the provided document.")
 50@click.option(
 51    "--graph",
 52    is_flag=True,
 53    default=False,
 54    help="Generate a relationship graph from the input file. "
 55    "The generated graph is saved to the file specified with --outfile. "
 56    "Note: You need to install the optional dependencies 'networkx' and 'pygraphviz' for this feature.",
 57)
 58def main(infile: str, outfile: str, version: str, novalidation: bool, graph: bool):
 59    """
 60    CLI-tool for validating SPDX documents and converting between RDF, TAG-VALUE, JSON, YAML and XML formats.
 61    Formats are determined by the file endings.
 62    To use, run: 'pyspdxtools --infile <input file name> --outfile <output file name>'
 63    """
 64    try:
 65        document: Document = parse_file(infile)
 66
 67        if not novalidation:
 68            if not version:
 69                version = document.creation_info.spdx_version
 70
 71            if version not in ["SPDX-2.2", "SPDX-2.3"]:
 72                logging.error(f"This tool only supports SPDX versions SPDX-2.2 and SPDX-2.3, but got: {version}")
 73                sys.exit(1)
 74
 75            validation_messages: List[ValidationMessage] = validate_full_spdx_document(document, version)
 76            if validation_messages:
 77                log_string = "\n".join(
 78                    ["The document is invalid. The following issues have been found:"]
 79                    + [message.validation_message for message in validation_messages]
 80                )
 81                logging.error(log_string)
 82                sys.exit(1)
 83            else:
 84                logging.info("The document is valid.")
 85
 86        if outfile == "-":
 87            tagvalue_writer.write_document(document, sys.stdout)
 88
 89        elif graph:
 90            try:
 91                export_graph_from_document(document, outfile)
 92            except ImportError:
 93                logging.error(
 94                    "To be able to draw a relationship graph of the parsed document "
 95                    "you need to install 'networkx' and 'pygraphviz'. Run 'pip install \".[graph_generation]\"'."
 96                )
 97                sys.exit(1)
 98
 99        elif outfile:
100            write_file(document, outfile, validate=False)
101
102    except NotImplementedError as err:
103        logging.error(
104            err.args[0]
105            + "\nPlease note that this project is currently undergoing a major refactoring and therefore missing "
106            "a few features which will be added in time (refer to https://github.com/spdx/tools-python/issues "
107            "for insights into the current status).\n"
108            "In the meantime, please use the current PyPI release version."
109        )
110        sys.exit(1)
111
112    except SPDXParsingError as err:
113        log_string = "\n".join(
114            ["There have been issues while parsing the provided document:"]
115            + [message for message in err.get_messages()]
116        )
117        logging.error(log_string)
118        sys.exit(1)
119
120    except JSONDecodeError as err:
121        logging.error(f"Invalid JSON provided: {err.args[0]}")
122        sys.exit(1)
123
124    except ScannerError as err:
125        logging.error("Invalid YAML provided: " + "\n".join([str(arg) for arg in err.args]))
126        sys.exit(1)
127
128    except ExpatError as err:
129        logging.error(f"Invalid XML provided: {err.args[0]}")
130        sys.exit(1)
131
132    except SAXParseException as err:
133        logging.error(f"Invalid RDF-XML provided: {str(err)}")
134        sys.exit(1)
135
136    except FileNotFoundError as err:
137        logging.error(f"{err.strerror}: {err.filename}")
138        sys.exit(1)
139
140
141if __name__ == "__main__":
142    main()
main = <Command main>

CLI-tool for validating SPDX documents and converting between RDF, TAG-VALUE, JSON, YAML and XML formats. Formats are determined by the file endings. To use, run: 'pyspdxtools --infile --outfile '