From 67d7bdca9fb749ec65e3d7291251a7414214145d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Mon, 2 Jan 2023 12:51:31 +0100 Subject: [PATCH 1/7] [issue-388] implement CLI parser/validator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/clitools/parser.py | 40 +++++++++++++++++++++++++++++++----- src/parser/parse_anything.py | 27 ++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 5 deletions(-) create mode 100644 src/parser/parse_anything.py diff --git a/src/clitools/parser.py b/src/clitools/parser.py index e4340115b..39444ec7b 100755 --- a/src/clitools/parser.py +++ b/src/clitools/parser.py @@ -11,23 +11,53 @@ # See the License for the specific language governing permissions and # limitations under the License. +import sys +from typing import List + import click -from src.model.spdx_no_assertion import SpdxNoAssertion -from src.model.spdx_none import SpdxNone +from src.model.document import Document +from src.parser.parse_anything import parse_file +from src.validation.document_validator import validate_full_spdx_document +from src.validation.validation_message import ValidationMessage +from src.writer.tagvalue.tagvalue_writer import write_document @click.command() @click.option("--file", prompt="File name", help="The file to be parsed") -@click.option("--force", is_flag=True, help="print information even if there are some parsing errors") -def main(file, force): +@click.option("--version", prompt="SPDX version", help="The SPDX version to be used during validation") +@click.option("--validate", is_flag=True, help="validate the provided document") +@click.option("--printout", is_flag=True, help="print the parsed document to stdout in tag-value format") +def main(file, version, validate, printout): """ COMMAND-LINE TOOL for parsing file of RDF, XML, JSON, YAML and XML format. To use : run `pyspdxtools_parser` using terminal or run `pyspdxtools_parser --file ` """ - raise NotImplementedError("Currently, no parsers are implemented") + try: + document: Document = parse_file(file) + except NotImplementedError as err: + print(err.args[0]) + print("Please note that this project is currently undergoing a major refactoring and therefore missing " + "a few features which will be added in time.\n" + "In the meanwhile, please use the current PyPI release version 0.7.0.") + return + + if printout: + write_document(document, sys.stdout) + print("") + + if validate: + validation_messages: List[ValidationMessage] = validate_full_spdx_document(document, version) + if validation_messages: + print("The document is invalid. The following issues have been found:") + for message in validation_messages: + print(message.validation_message) + else: + print("The document is valid.") + + # raise NotImplementedError("Currently, no parsers are implemented") # Parse document # First one to implement is the Json parser: https://github.com/spdx/tools-python/issues/305 diff --git a/src/parser/parse_anything.py b/src/parser/parse_anything.py new file mode 100644 index 000000000..b3796ce2c --- /dev/null +++ b/src/parser/parse_anything.py @@ -0,0 +1,27 @@ +# Copyright (c) spdx contributors +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from src.parser.error import SPDXParsingError +from src.parser.json.json_parser import JsonParser + + +def parse_file(file_name: str): + if file_name.endswith(".rdf") or file_name.endswith(".rdf.xml"): + raise NotImplementedError("Currently, the rdf parser is not implemented") + elif file_name.endswith(".tag") or file_name.endswith(".spdx"): + raise NotImplementedError("Currently, the tag-value parser is not implemented") + elif file_name.endswith(".json"): + return JsonParser().parse(file_name) + elif file_name.endswith(".xml"): + raise NotImplementedError("Currently, the xml parser is not implemented") + elif file_name.endswith(".yaml") or file_name.endswith(".yml"): + raise NotImplementedError("Currently, the yaml parser is not implemented") + else: + raise SPDXParsingError(["Unsupported file type: " + str(file_name)]) From 52c9f177b98e1db902ce87627664d934362ae562 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Mon, 2 Jan 2023 15:05:28 +0100 Subject: [PATCH 2/7] [issue-388] introduce format enum and write_anything.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/formats.py | 36 ++++++++++++++++++++++++++++++++++++ src/parser/parse_anything.py | 15 +++++++-------- src/writer/write_anything.py | 27 +++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 8 deletions(-) create mode 100644 src/formats.py create mode 100644 src/writer/write_anything.py diff --git a/src/formats.py b/src/formats.py new file mode 100644 index 000000000..37f7400c1 --- /dev/null +++ b/src/formats.py @@ -0,0 +1,36 @@ +# Copyright (c) 2022 spdx contributors +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from enum import Enum, auto + +from src.parser.error import SPDXParsingError + + +class FileFormat(Enum): + JSON = auto() + YAML = auto() + XML = auto() + TAG_VALUE = auto() + RDF = auto() + + +def file_name_to_format(file_name: str) -> FileFormat: + if file_name.endswith(".rdf") or file_name.endswith(".rdf.xml"): + return FileFormat.RDF + elif file_name.endswith(".tag") or file_name.endswith(".spdx"): + return FileFormat.TAG_VALUE + elif file_name.endswith(".json"): + return FileFormat.JSON + elif file_name.endswith(".xml"): + return FileFormat.XML + elif file_name.endswith(".yaml") or file_name.endswith(".yml"): + return FileFormat.YAML + else: + raise SPDXParsingError(["Unsupported SPDX file type: " + str(file_name)]) diff --git a/src/parser/parse_anything.py b/src/parser/parse_anything.py index b3796ce2c..29bef0517 100644 --- a/src/parser/parse_anything.py +++ b/src/parser/parse_anything.py @@ -8,20 +8,19 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -from src.parser.error import SPDXParsingError +from src.formats import file_name_to_format, FileFormat from src.parser.json.json_parser import JsonParser def parse_file(file_name: str): - if file_name.endswith(".rdf") or file_name.endswith(".rdf.xml"): + input_format = file_name_to_format(file_name) + if input_format == FileFormat.RDF: raise NotImplementedError("Currently, the rdf parser is not implemented") - elif file_name.endswith(".tag") or file_name.endswith(".spdx"): + elif input_format == FileFormat.TAG_VALUE: raise NotImplementedError("Currently, the tag-value parser is not implemented") - elif file_name.endswith(".json"): + elif input_format == FileFormat.JSON: return JsonParser().parse(file_name) - elif file_name.endswith(".xml"): + elif input_format == FileFormat.XML: raise NotImplementedError("Currently, the xml parser is not implemented") - elif file_name.endswith(".yaml") or file_name.endswith(".yml"): + elif input_format == FileFormat.YAML: raise NotImplementedError("Currently, the yaml parser is not implemented") - else: - raise SPDXParsingError(["Unsupported file type: " + str(file_name)]) diff --git a/src/writer/write_anything.py b/src/writer/write_anything.py new file mode 100644 index 000000000..b245d363c --- /dev/null +++ b/src/writer/write_anything.py @@ -0,0 +1,27 @@ +# Copyright (c) 2022 spdx contributors +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from src.formats import file_name_to_format, FileFormat +from src.model.document import Document +from src.writer.tagvalue import tagvalue_writer + + +def write_file(document: Document, file_name: str): + output_format = file_name_to_format(file_name) + if output_format == FileFormat.JSON: + raise NotImplementedError("Currently, the json writer is not implemented") + elif output_format == FileFormat.YAML: + raise NotImplementedError("Currently, the yaml writer is not implemented") + elif output_format == FileFormat.XML: + raise NotImplementedError("Currently, the xml writer is not implemented") + elif output_format == FileFormat.TAG_VALUE: + tagvalue_writer.write_document_to_file(document, file_name) + elif output_format == FileFormat.RDF: + raise NotImplementedError("Currently, the rdf writer is not implemented") From 8b03c250775293091b470c08e9344a203aae5bf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Mon, 2 Jan 2023 17:03:01 +0100 Subject: [PATCH 3/7] [issue-388] implement CLI convertor and rewrite docstrings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/clitools/convertor.py | 101 +++++++------------------------------- src/clitools/parser.py | 12 +---- 2 files changed, 18 insertions(+), 95 deletions(-) diff --git a/src/clitools/convertor.py b/src/clitools/convertor.py index 2a9ecb0f5..12ac2cb74 100644 --- a/src/clitools/convertor.py +++ b/src/clitools/convertor.py @@ -11,100 +11,33 @@ # See the License for the specific language governing permissions and # limitations under the License. -import os - import click - -def print_help_msg(command): - with click.Context(command) as ctx: - click.echo(command.get_help(ctx)) - - -def determine_infile_and_outfile(infile, outfile, src, from_, to): - if infile is not None and outfile is not None: - """ - when the CLI is of given format: - ' pyspdxtools_convertor ---infile ---outfile . - """ - return infile, outfile - - elif infile is None and outfile is None and len(src) == 2: - """ - ' pyspdxtools_convertor -f/--from -t/--to . - """ - infile = src[0] - outfile = src[1] - if from_ is not None: - infile_path = os.path.splitext(infile)[0] - infile = infile_path + "." + from_ - if to is not None: - outfile_path = os.path.splitext(outfile)[0] - outfile = outfile_path + "." + to - return infile, outfile - - elif infile is None and outfile is not None: - """ - ' pyspdxtools_convertor -f/--from --outfile ' - """ - infile = src[0] - if from_ is not None: - infile_path = os.path.splitext(infile)[0] - infile = infile_path + "." + from_ - return infile, outfile - - elif infile is not None and outfile is None: - """ - ' pyspdxtools_convertor --infile -t/--to ' - """ - outfile = src[0] - if to is not None: - outfile_path = os.path.splitext(outfile)[0] - outfile = outfile_path + "." + to - return infile, outfile - - else: - raise ValueError("Given arguments for convertor are invalid.") +from src.model.document import Document +from src.parser.parse_anything import parse_file +from src.writer.write_anything import write_file @click.command() -@click.argument("src", nargs=-1) -@click.option("--infile", "-i", help="The file to be converted ") -@click.option("--outfile", "-o", help="The file after converting") -@click.option( - "--to", - "-t", - type=click.Choice(["json", "rdf", "yaml", "xml", "tag"], case_sensitive=False) -) -@click.option( - "--from", - "-f", - "from_", - type=click.Choice(["tag", "rdf"], case_sensitive=False)) -@click.option("--force", is_flag=True, help="convert even if there are some parsing errors or inconsistencies") -def main(infile, outfile, src, from_, to, force): +@click.option("--infile", "-i", help="The file containing the document to be converted") +@click.option("--outfile", "-o", help="The file to write the converted document to") +def main(infile, outfile): """ - CLI-TOOL for converting a RDF or TAG file to RDF, JSON, YAML, TAG or XML format. - - To use : run 'pyspdxtools_convertor -f -t ' command on terminal - or use ' pyspdxtools_convertor --infile --outfile ' - + CLI-tool for converting SPDX documents between RDF, TAG-VALUE, JSON, YAML and XML formats. + Formats are determined by the file endings. + To use, run: 'pyspdxtools_convertor --infile --outfile ' """ try: - infile, outfile = determine_infile_and_outfile(infile, outfile, src, from_, to) - except ValueError as err: - print(err) - print_help_msg(main) + document: Document = parse_file(infile) + + write_file(document, outfile) + except NotImplementedError as err: + print(err.args[0]) + print("Please note that this project is currently undergoing a major refactoring and therefore missing " + "a few features which will be added in time.\n" + "In the meanwhile, please use the current PyPI release version 0.7.0.") return - raise NotImplementedError("Currently, conversion is not implemented") - - # Parse document from infile - # First one to implement is the Json parser: https://github.com/spdx/tools-python/issues/305 - - # Write document to outfile - # First writer to implement is the Json writer: https://github.com/spdx/tools-python/issues/359 - if __name__ == "__main__": main() diff --git a/src/clitools/parser.py b/src/clitools/parser.py index 39444ec7b..284170009 100755 --- a/src/clitools/parser.py +++ b/src/clitools/parser.py @@ -30,10 +30,8 @@ @click.option("--printout", is_flag=True, help="print the parsed document to stdout in tag-value format") def main(file, version, validate, printout): """ - COMMAND-LINE TOOL for parsing file of RDF, XML, JSON, YAML and XML format. - + CLI-tool for parsing file of RDF, TAG-VALUE, JSON, YAML and XML format. To use : run `pyspdxtools_parser` using terminal or run `pyspdxtools_parser --file ` - """ try: document: Document = parse_file(file) @@ -57,14 +55,6 @@ def main(file, version, validate, printout): else: print("The document is valid.") - # raise NotImplementedError("Currently, no parsers are implemented") - - # Parse document - # First one to implement is the Json parser: https://github.com/spdx/tools-python/issues/305 - - # Print all document properties - or possibly a selection of them. Should be human-readable, so using indentation - # for nested properties is probably a good idea. - if __name__ == "__main__": main() From 043bfa39a10f5f6f879230aee4ea21ee67e47dbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Mon, 2 Jan 2023 17:10:32 +0100 Subject: [PATCH 4/7] [issue-388] remove obsolete tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- tests/clitools/__init__.py | 0 tests/clitools/test_cli_convertor.py | 82 ---------------------------- 2 files changed, 82 deletions(-) delete mode 100644 tests/clitools/__init__.py delete mode 100644 tests/clitools/test_cli_convertor.py diff --git a/tests/clitools/__init__.py b/tests/clitools/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/clitools/test_cli_convertor.py b/tests/clitools/test_cli_convertor.py deleted file mode 100644 index d4ea5c760..000000000 --- a/tests/clitools/test_cli_convertor.py +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright (c) 2022 spdx tool contributors -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# http://www.apache.org/licenses/LICENSE-2.0 -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from unittest import TestCase - -import pytest - -from src.clitools.convertor import determine_infile_and_outfile - - -class TestConvertor(TestCase): - maxDiff = None - - def test_determine_input_with_known_i_o_format(self): - infile_given = 'infile.rdf' - outfile_given = 'outfile.json' - src = () - from_ = None - to = None - - infile, outfile = determine_infile_and_outfile(infile_given, outfile_given, src, from_, to) - - assert infile == infile_given - assert outfile == outfile_given - - def test_determine_input_with_unknown_i_o_format(self): - infile_given = None - outfile_given = None - src = ('infile.in', 'outfile.out') - from_ = 'rdf' - to = 'json' - expected_infile = 'infile.rdf' - expected_outfile = 'outfile.json' - - infile, outfile = determine_infile_and_outfile(infile_given, outfile_given, src, from_, to) - - assert infile == expected_infile - assert outfile == expected_outfile - - def test_determine_input_with_known_i_format_unknown_o_format(self): - infile_given = 'infile.rdf' - outfile_given = None - src = ('outfile',) - from_ = None - to = 'json' - expected_outfile = 'outfile.json' - - infile, outfile = determine_infile_and_outfile(infile_given, outfile_given, src, from_, to) - - assert infile == infile_given - assert outfile == expected_outfile - - def test_determine_input_with_unknown_i_format_known_o_format(self): - infile_given = None - outfile_given = 'outfile.json' - src = ('infile',) - from_ = 'rdf' - to = None - expected_infile = 'infile.rdf' - - infile, outfile = determine_infile_and_outfile(infile_given, outfile_given, src, from_, to) - - assert infile == expected_infile - assert outfile == outfile_given - - def test_determine_input_with_invalid_arguments(self): - infile_given = None - outfile_given = None - src = () - from_ = None - to = None - - with pytest.raises(ValueError): - determine_infile_and_outfile(infile_given, outfile_given, src, from_, to) From ca81a129955ad5b6df5d8692ea0a2a636206cb07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Tue, 3 Jan 2023 09:15:05 +0100 Subject: [PATCH 5/7] [issue-388] include json writer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/writer/write_anything.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/writer/write_anything.py b/src/writer/write_anything.py index b245d363c..319f214f5 100644 --- a/src/writer/write_anything.py +++ b/src/writer/write_anything.py @@ -10,13 +10,14 @@ # limitations under the License. from src.formats import file_name_to_format, FileFormat from src.model.document import Document +from src.writer.json import json_writer from src.writer.tagvalue import tagvalue_writer def write_file(document: Document, file_name: str): output_format = file_name_to_format(file_name) if output_format == FileFormat.JSON: - raise NotImplementedError("Currently, the json writer is not implemented") + json_writer.write_document(document, file_name) elif output_format == FileFormat.YAML: raise NotImplementedError("Currently, the yaml writer is not implemented") elif output_format == FileFormat.XML: From ba2ccffed4012b399be5902d7bb4f8602be4cbcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Tue, 3 Jan 2023 15:56:17 +0100 Subject: [PATCH 6/7] [issue-388] rework the CLI user flow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/clitools/convertor.py | 42 ++++++++++++++++++++++++++++-------- src/formats.py | 4 ++-- src/parser/parse_anything.py | 2 +- src/writer/write_anything.py | 6 +++--- 4 files changed, 39 insertions(+), 15 deletions(-) diff --git a/src/clitools/convertor.py b/src/clitools/convertor.py index 12ac2cb74..df82cb1f6 100644 --- a/src/clitools/convertor.py +++ b/src/clitools/convertor.py @@ -10,33 +10,57 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +import sys +from typing import List import click from src.model.document import Document from src.parser.parse_anything import parse_file +from src.validation.document_validator import validate_full_spdx_document +from src.validation.validation_message import ValidationMessage +from src.writer.tagvalue import tagvalue_writer from src.writer.write_anything import write_file @click.command() -@click.option("--infile", "-i", help="The file containing the document to be converted") -@click.option("--outfile", "-o", help="The file to write the converted document to") -def main(infile, outfile): +@click.option("--infile", "-i", prompt="input file path", help="The file containing the document to be validated or converted.") +@click.option("--outfile", "-o", help="The file to write the converted document to (write a dash for output to stdout or omit for no conversion).") +@click.option("--version", help='The SPDX version to be used during parsing and validation (format "SPDX-2.3").', default="SPDX-2.3") +@click.option("--novalidation", is_flag=True, help="Don't validate the provided document.") +def main(infile: str, outfile: str, version: str, novalidation: bool): """ - CLI-tool for converting SPDX documents between RDF, TAG-VALUE, JSON, YAML and XML formats. + 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_convertor --infile --outfile ' + To use, run: 'pyspdxtools_convertor --infile --outfile ' """ try: document: Document = parse_file(infile) - write_file(document, outfile) + if outfile == "-": + tagvalue_writer.write_document(document, sys.stdout) + print("") + + if not novalidation: + validation_messages: List[ValidationMessage] = validate_full_spdx_document(document, version) + if validation_messages: + print("The document is invalid. The following issues have been found:") + for message in validation_messages: + print(message.validation_message) + sys.exit(1) + else: + print("The document is valid.") + + if outfile and outfile != "-": + write_file(document, outfile, validate=False) + except NotImplementedError as err: print(err.args[0]) print("Please note that this project is currently undergoing a major refactoring and therefore missing " - "a few features which will be added in time.\n" - "In the meanwhile, please use the current PyPI release version 0.7.0.") - return + "a few features which will be added in time (refer to https://github.com/spdx/tools-python/issues " + "for insights into the current status).\n" + "In the meantime, please use the PyPI release version 0.7.0.") + sys.exit(1) if __name__ == "__main__": diff --git a/src/formats.py b/src/formats.py index 37f7400c1..fecaf2c24 100644 --- a/src/formats.py +++ b/src/formats.py @@ -18,12 +18,12 @@ class FileFormat(Enum): YAML = auto() XML = auto() TAG_VALUE = auto() - RDF = auto() + RDF_XML = auto() def file_name_to_format(file_name: str) -> FileFormat: if file_name.endswith(".rdf") or file_name.endswith(".rdf.xml"): - return FileFormat.RDF + return FileFormat.RDF_XML elif file_name.endswith(".tag") or file_name.endswith(".spdx"): return FileFormat.TAG_VALUE elif file_name.endswith(".json"): diff --git a/src/parser/parse_anything.py b/src/parser/parse_anything.py index 29bef0517..541188a59 100644 --- a/src/parser/parse_anything.py +++ b/src/parser/parse_anything.py @@ -14,7 +14,7 @@ def parse_file(file_name: str): input_format = file_name_to_format(file_name) - if input_format == FileFormat.RDF: + if input_format == FileFormat.RDF_XML: raise NotImplementedError("Currently, the rdf parser is not implemented") elif input_format == FileFormat.TAG_VALUE: raise NotImplementedError("Currently, the tag-value parser is not implemented") diff --git a/src/writer/write_anything.py b/src/writer/write_anything.py index 319f214f5..ca01ba86e 100644 --- a/src/writer/write_anything.py +++ b/src/writer/write_anything.py @@ -14,15 +14,15 @@ from src.writer.tagvalue import tagvalue_writer -def write_file(document: Document, file_name: str): +def write_file(document: Document, file_name: str, validate: bool = True): output_format = file_name_to_format(file_name) if output_format == FileFormat.JSON: - json_writer.write_document(document, file_name) + json_writer.write_document(document, file_name, validate) elif output_format == FileFormat.YAML: raise NotImplementedError("Currently, the yaml writer is not implemented") elif output_format == FileFormat.XML: raise NotImplementedError("Currently, the xml writer is not implemented") elif output_format == FileFormat.TAG_VALUE: tagvalue_writer.write_document_to_file(document, file_name) - elif output_format == FileFormat.RDF: + elif output_format == FileFormat.RDF_XML: raise NotImplementedError("Currently, the rdf writer is not implemented") From a6d693ec2159d5bbddd303c3aa6676dd40337656 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Tue, 3 Jan 2023 16:10:23 +0100 Subject: [PATCH 7/7] [issue-388] merge parser and convertor into a single tool and update the README MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- README.md | 34 ++++------- pyproject.toml | 3 +- src/clitools/parser.py | 60 ------------------- src/clitools/{convertor.py => pyspdxtools.py} | 2 +- 4 files changed, 13 insertions(+), 86 deletions(-) delete mode 100755 src/clitools/parser.py rename src/clitools/{convertor.py => pyspdxtools.py} (96%) diff --git a/README.md b/README.md index d467ea3b7..e4a12ad49 100644 --- a/README.md +++ b/README.md @@ -58,34 +58,22 @@ This is the result of an initial GSoC contribution by @[ah450](https://github.co ## Command-line usage: -1. **PARSER** (for parsing any format): +1. **PARSING/VALIDATING** (for parsing any format): -* Use `pyspdxtools_parser --file ` where `` is the location of the file. -If you are using a source distribution, try running: `pyspdxtools_parser --file tests/data/formats/SPDXRdfExample.rdf`. +* Use `pyspdxtools -i ` where `` is the location of the file. + If you are using a source distribution, try running: `pyspdxtools -i tests/data/formats/SPDXJSONExample-v2.3.spdx.json`. -* Or you can use `pyspdxtools_parser` only, and it will automatically prompt/ask for `filename`. +* Or you can use `pyspdxtools` only, and it will automatically prompt/ask for the `input file path`. -* For help use `pyspdxtools_parser --help` +2. **CONVERTING** (for converting one format to another): +* Use `pyspdxtools -i -o ` where `` is the location of the file to be converted + and `` is the location of the output file. The output format is inferred automatically from the file ending. + If you are using a source distribution, try running : `pyspdxtools -i tests/data/formats/SPDXJSONExample-v2.3.spdx.json -o output.tag` -2. **CONVERTOR** (for converting one format to another): - -* If I/O formats are known: - - * Use `pyspdxtools_convertor --infile/-i --outfile/-o ` where `` is the location of the file to be converted - and `` is the location of the output file. - If you are using a source distribution, try running : `pyspdxtools_convertor --infile tests/data/formats/SPDXRdfExample.rdf --outfile output.json` - -* If I/O formats are not known: - - * Use `pyspdxtools_convertor --from/-f --to/-t ` where `` is the manually entered format of the input file - and `` is the manually entered format of the output file. - If you are using a source distribution, try running : `pyspdxtools_convertor --from tag tests/data/formats/SPDXTagExample.in --to yaml output.out` - -* If one of the formats is known and the other is not, you can use a mixture of the above two points. -Example (if you are using a source distribution): `pyspdxtools_convertor -f rdf tests/data/formats/SPDXRdfExample.xyz -o output.xml` - -* For help use `pyspdxtools_convertor --help` +* If you want to skip the validation process, provide the `--novalidation` flag, like so: + `pyspdxtools -i tests/data/formats/SPDXJSONExample-v2.3.spdx.json -o output.tag --novalidation` +* For help use `pyspdxtools --help` # Installation diff --git a/pyproject.toml b/pyproject.toml index 8177df578..4d03a65d2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,8 +31,7 @@ dynamic = ["version"] test = ["pytest"] [project.scripts] -pyspdxtools_convertor = "src.clitools.convertor:main" -pyspdxtools_parser = "src.clitools.parser:main" +pyspdxtools = "src.clitools.pyspdxtools:main" [tool.setuptools] zip-safe = false # because of the uses of __file__: https://github.com/spdx/tools-python/issues/257 diff --git a/src/clitools/parser.py b/src/clitools/parser.py deleted file mode 100755 index 284170009..000000000 --- a/src/clitools/parser.py +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright (c) 2020 Yash Varshney -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# http://www.apache.org/licenses/LICENSE-2.0 -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import sys -from typing import List - -import click - -from src.model.document import Document -from src.parser.parse_anything import parse_file -from src.validation.document_validator import validate_full_spdx_document -from src.validation.validation_message import ValidationMessage -from src.writer.tagvalue.tagvalue_writer import write_document - - -@click.command() -@click.option("--file", prompt="File name", help="The file to be parsed") -@click.option("--version", prompt="SPDX version", help="The SPDX version to be used during validation") -@click.option("--validate", is_flag=True, help="validate the provided document") -@click.option("--printout", is_flag=True, help="print the parsed document to stdout in tag-value format") -def main(file, version, validate, printout): - """ - CLI-tool for parsing file of RDF, TAG-VALUE, JSON, YAML and XML format. - To use : run `pyspdxtools_parser` using terminal or run `pyspdxtools_parser --file ` - """ - try: - document: Document = parse_file(file) - except NotImplementedError as err: - print(err.args[0]) - print("Please note that this project is currently undergoing a major refactoring and therefore missing " - "a few features which will be added in time.\n" - "In the meanwhile, please use the current PyPI release version 0.7.0.") - return - - if printout: - write_document(document, sys.stdout) - print("") - - if validate: - validation_messages: List[ValidationMessage] = validate_full_spdx_document(document, version) - if validation_messages: - print("The document is invalid. The following issues have been found:") - for message in validation_messages: - print(message.validation_message) - else: - print("The document is valid.") - - -if __name__ == "__main__": - main() diff --git a/src/clitools/convertor.py b/src/clitools/pyspdxtools.py similarity index 96% rename from src/clitools/convertor.py rename to src/clitools/pyspdxtools.py index df82cb1f6..d9dbed945 100644 --- a/src/clitools/convertor.py +++ b/src/clitools/pyspdxtools.py @@ -32,7 +32,7 @@ def main(infile: str, outfile: str, version: str, novalidation: bool): """ 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_convertor --infile --outfile ' + To use, run: 'pyspdxtools --infile --outfile ' """ try: document: Document = parse_file(infile)