From aa225fbc5b599a52880708a0c3a0c48e34fc2263 Mon Sep 17 00:00:00 2001 From: Zalan Blenessy Date: Tue, 1 Jul 2025 11:20:55 +0200 Subject: [PATCH 01/23] [issue-854] Remove some control characters from JSON SPDX Signed-off-by: Zalan Blenessy --- .../spdx/parser/json/json_parser.py | 23 +++++++++- tests/spdx/data/ControlCharacters.spdx.json | 46 +++++++++++++++++++ .../parser/jsonlikedict/test_json_parser.py | 11 +++++ 3 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 tests/spdx/data/ControlCharacters.spdx.json create mode 100644 tests/spdx/parser/jsonlikedict/test_json_parser.py diff --git a/src/spdx_tools/spdx/parser/json/json_parser.py b/src/spdx_tools/spdx/parser/json/json_parser.py index 219ccfed2..675ed2560 100644 --- a/src/spdx_tools/spdx/parser/json/json_parser.py +++ b/src/spdx_tools/spdx/parser/json/json_parser.py @@ -3,14 +3,33 @@ # SPDX-License-Identifier: Apache-2.0 import json -from beartype.typing import Dict +from beartype.typing import Any, Dict from spdx_tools.spdx.model import Document from spdx_tools.spdx.parser.jsonlikedict.json_like_dict_parser import JsonLikeDictParser +# chars we don't want to see in SBOMs +CONTROL_CHARS_MAP = { + 8: None, # ASCII/UTF-8: backspace + 12: None, # ASCII/UTF-8: formfeed +} + + +def remove_control_chars_from_value(value: Any) -> Any: + if isinstance(value, str): + return value.translate(CONTROL_CHARS_MAP) + elif isinstance(value, list): + for i in range(len(value)): + value[i] = remove_control_chars_from_value(value[i]) + return value + + +def remove_json_control_chars_hook(pairs: list) -> dict: + return {k: remove_control_chars_from_value(v) for k, v in pairs} + def parse_from_file(file_name: str, encoding: str = "utf-8") -> Document: with open(file_name, encoding=encoding) as file: - input_doc_as_dict: Dict = json.load(file) + input_doc_as_dict: Dict = json.load(file, object_pairs_hook=remove_json_control_chars_hook) return JsonLikeDictParser().parse(input_doc_as_dict) diff --git a/tests/spdx/data/ControlCharacters.spdx.json b/tests/spdx/data/ControlCharacters.spdx.json new file mode 100644 index 000000000..d79ee2e92 --- /dev/null +++ b/tests/spdx/data/ControlCharacters.spdx.json @@ -0,0 +1,46 @@ +{ + "spdxVersion": "SPDX-2.2", + "dataLicense": "CC0-1.0", + "SPDXID": "SPDXRef-DOCUMENT", + "creationInfo": { + "created": "2020-11-24T01:12:27Z", + "creators": ["Person: Nisha \b\f K (nishak@vmware.com)"] + }, + "name": "golang-dist", + "documentNamespace": "https://swinslow.net/spdx-examples/example7/golang-dist-492dfde4-318b-49f7-b48c-934bfafbde48", + "documentDescribes": ["SPDXRef-golang-dist"], + "packages": [ + { + "name": "go1.16.4.linux-amd64", + "SPDXID": "SPDXRef-golang-dist", + "downloadLocation": "https://golang.org/dl/go1.16.4.linux-amd64.tar.gz", + "versionInfo": "1.16.4", + "filesAnalyzed": false, + "checksums": [ + { + "algorithm": "SHA256", + "checksumValue": "7154e88f5a8047aad4b80ebace58a059e36e7e2e4eb3b383127a28c711b4ff59" + } + ], + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "LicenseRef-Golang-BSD-plus-Patents", + "copyrightText": "Copyright (c) 2009 The Go Authors. \b All rights reserved." + }, + { + "name": "go", + "SPDXID": "SPDXRef-go-compiler", + "downloadLocation": "https://golang.org/dl/go1.16.4.linux-amd64.tar.gz", + "versionInfo": "1.16.4", + "filesAnalyzed": false, + "licenseConcluded": "NOASSERTION", + "licenseDeclared": "NOASSERTION", + "copyrightText": "NOASSERTION" + } + ], + "hasExtractedLicensingInfos": [ + { + "licenseId": "LicenseRef-Golang-BSD-plus-Patents", + "extractedText": "Golang BSD plus Patents \"\\\/\b\f\n\r\t" + } + ] +} diff --git a/tests/spdx/parser/jsonlikedict/test_json_parser.py b/tests/spdx/parser/jsonlikedict/test_json_parser.py new file mode 100644 index 000000000..ab3249d63 --- /dev/null +++ b/tests/spdx/parser/jsonlikedict/test_json_parser.py @@ -0,0 +1,11 @@ +import os + +from spdx_tools.spdx.parser.json import json_parser + + +def test_parse_control_characters(): + doc = json_parser.parse_from_file( + os.path.join(os.path.dirname(__file__), "../../data/ControlCharacters.spdx.json") + ) + assert doc.creation_info.creators[0].name == "Nisha K" + assert doc.extracted_licensing_info[0].extracted_text == 'Golang BSD plus Patents "\\/\n\r\t' From 47751b680e4cea0f9ad035d37619f598c34af736 Mon Sep 17 00:00:00 2001 From: konsulten Date: Mon, 30 Sep 2024 13:56:44 +0200 Subject: [PATCH 02/23] downloadLocation URIs not case sensitive Signed-off-by: Claes Nordmark --- src/spdx_tools/spdx/validation/uri_validators.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/spdx_tools/spdx/validation/uri_validators.py b/src/spdx_tools/spdx/validation/uri_validators.py index c14d196f4..b2bc91692 100644 --- a/src/spdx_tools/spdx/validation/uri_validators.py +++ b/src/spdx_tools/spdx/validation/uri_validators.py @@ -18,6 +18,7 @@ download_location_pattern = ( "^(((" + supported_download_repos + "\\+)?" + url_pattern + ")|" + git_pattern + "|" + bazaar_pattern + ")$" ) +compiled_pattern = re.compile(download_location_pattern, re.IGNORECASE) def validate_url(url: str) -> List[str]: @@ -28,7 +29,7 @@ def validate_url(url: str) -> List[str]: def validate_download_location(location: str) -> List[str]: - if not (validate_url(location) == [] or re.match(download_location_pattern, location)): + if not (validate_url(location) == [] or compiled_pattern.match(location)): return [f"must be a valid URL or download location according to the specification, but is: {location}"] return [] From 0b5210acc5a60c44a857259b6356973196feefbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Tue, 8 Jul 2025 10:11:33 +0200 Subject: [PATCH 03/23] [issue-825] Allow capital letters in validate_url() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- src/spdx_tools/spdx/validation/uri_validators.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/spdx_tools/spdx/validation/uri_validators.py b/src/spdx_tools/spdx/validation/uri_validators.py index b2bc91692..7720c3fb5 100644 --- a/src/spdx_tools/spdx/validation/uri_validators.py +++ b/src/spdx_tools/spdx/validation/uri_validators.py @@ -12,6 +12,8 @@ "\\/\\/|ftp:\\/\\/)?([\\w\\-.!~*'()%;:&=+$,]+@)?[a-z0-9]+([\\-\\.]{1}[a-z0-9]+){0,100}\\.[a-z]{2,5}" "(:[0-9]{1,5})?(\\/.*)?" ) +url_pattern_ignore_case = re.compile(url_pattern, re.IGNORECASE) + supported_download_repos: str = "(git|hg|svn|bzr)" git_pattern = "(git\\+git@[a-zA-Z0-9\\.\\-]+:[a-zA-Z0-9/\\\\.@\\-]+)" bazaar_pattern = "(bzr\\+lp:[a-zA-Z0-9\\.\\-]+)" @@ -22,7 +24,7 @@ def validate_url(url: str) -> List[str]: - if not re.match(url_pattern, url): + if not url_pattern_ignore_case.match(url): return [f"must be a valid URL, but is: {url}"] return [] From b7f9a3defe8b0af1414cd75f1447ec242428f1d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Tue, 8 Jul 2025 10:12:29 +0200 Subject: [PATCH 04/23] [issue-825] Add tests for allowing capital letters in URL/URIs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- tests/spdx/validation/test_uri_validators.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/spdx/validation/test_uri_validators.py b/tests/spdx/validation/test_uri_validators.py index 2d374ee8e..0fb4ec7cb 100644 --- a/tests/spdx/validation/test_uri_validators.py +++ b/tests/spdx/validation/test_uri_validators.py @@ -14,6 +14,7 @@ "https://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82...", "http://some.url", "http://ftp.gnu.org/gnu/glibc/glibc-ports-2.15.tar.gz", + "HTTP://SOME.URL", ], ) def test_valid_url(input_value): @@ -79,6 +80,7 @@ def test_invalid_url(input_value): "bzr+https://bzr.myproject.org/MyProject/trunk@2019", "bzr+http://bzr.myproject.org/MyProject/trunk@v1.0", "bzr+https://bzr.myproject.org/MyProject/trunk@2019#src/somefile.c", + "BZR+HTTPS://BZR.MYPROJECT.ORG/MYPROJECT/TRUNK@2019#SRC/SOMEFILE.C", ], ) def test_valid_package_download_location(input_value): @@ -106,6 +108,7 @@ def test_invalid_package_download_location(input_value): "https://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82...", "h://someweirdtest^?", "https://some.uri that goes on!?", + "HTtPS://SOME.URI With CAPITALS", ], ) def test_valid_uri(input_value): From a0016e2d7fe8b2a72126a10eb36b470bfbf074da Mon Sep 17 00:00:00 2001 From: Olivier Benjamin Date: Fri, 5 Sep 2025 14:55:05 +0200 Subject: [PATCH 05/23] README.md: State that the experimental SPDX3 support is write-only. As described in issue #760, the tools do not currently support reading SPDX3.0 documents. This makes it clearer in the README. Signed-off-by: Olivier Benjamin (Schneider Electric) --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 99112ea02..e089bd138 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,8 @@ The main features of v0.8 are: stable at this point, as the spec is still evolving. SPDX3-related code is contained in a separate subpackage "spdx3" and its use is optional. We do not recommend using it in production code yet. +Note that v0.8 only supports **writing**, not **reading** SPDX 3.0 documents. +See [#760](https://github.com/spdx/tools-python/issues/760) for details. # Information From 92a62cf78db49a30a3b3b84a8c70058d0448502b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Fri, 9 Jan 2026 14:54:10 +0100 Subject: [PATCH 06/23] update GitHub Action: upload pages artifact MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * v1 uses the deprecated version v3 of upload artifact Signed-off-by: Armin Tänzer --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 095f9ec7a..95530f802 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -28,7 +28,7 @@ jobs: - name: Generate docs run: pdoc spdx_tools -o docs/ - name: Upload docs as artifact - uses: actions/upload-pages-artifact@v1 + uses: actions/upload-pages-artifact@v3 with: path: docs/ From 8a4c3ecc704f972e4c8af81c7ffae67e2329ce63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Fri, 9 Jan 2026 15:03:05 +0100 Subject: [PATCH 07/23] update GitHub workflows to Python 3.14, drop support for 3.8 and 3.9 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Python 3.9 reached end of life in 2025 Signed-off-by: Armin Tänzer --- .github/workflows/check_codestyle.yml | 2 +- .github/workflows/docs.yml | 2 +- .github/workflows/install_and_test.yml | 2 +- .github/workflows/integration_test.yml | 4 ++-- .github/workflows/prepare_release.yml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/check_codestyle.yml b/.github/workflows/check_codestyle.yml index 08b5e125f..327f2f54a 100644 --- a/.github/workflows/check_codestyle.yml +++ b/.github/workflows/check_codestyle.yml @@ -20,7 +20,7 @@ jobs: strategy: matrix: os: [ ubuntu-latest, macos-latest, windows-latest ] - python-version: [ "3.8", "3.9", "3.10", "3.11" ] + python-version: [ "3.14" ] steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 95530f802..be0d749b1 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -19,7 +19,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v4 with: - python-version: '3.11' + python-version: '3.14' - name: Install dependencies run: | sudo apt-get install graphviz-dev diff --git a/.github/workflows/install_and_test.yml b/.github/workflows/install_and_test.yml index 2149dd267..d661745be 100644 --- a/.github/workflows/install_and_test.yml +++ b/.github/workflows/install_and_test.yml @@ -17,7 +17,7 @@ jobs: strategy: matrix: os: [ ubuntu-latest, macos-latest, windows-latest ] - python-version: [ "3.8", "3.9", "3.10", "3.11" ] + python-version: [ "3.10", "3.11", "3.12", "3.13", "3.14" ] steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/integration_test.yml b/.github/workflows/integration_test.yml index 2a37fa7a6..3a749a3f0 100644 --- a/.github/workflows/integration_test.yml +++ b/.github/workflows/integration_test.yml @@ -13,10 +13,10 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Set up Python 3.11 + - name: Set up Python 3.14 uses: actions/setup-python@v4 with: - python-version: 3.11 + python-version: 3.14 - name: Installation run: | python -m pip install --upgrade pip diff --git a/.github/workflows/prepare_release.yml b/.github/workflows/prepare_release.yml index cb2f5a67f..15439f41f 100644 --- a/.github/workflows/prepare_release.yml +++ b/.github/workflows/prepare_release.yml @@ -19,7 +19,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v4 with: - python-version: '3.8' + python-version: '3.14' - name: Set up dependencies run: | python -m pip install --upgrade pip From e9019e039eae296ee99b506597a97fdafdb88d33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Fri, 9 Jan 2026 15:08:28 +0100 Subject: [PATCH 08/23] [issue-866] fix annotation access from class instance to class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This was a bug in the old code that only now throws an error in Python 3.14. Signed-off-by: Armin Tänzer --- src/spdx_tools/spdx3/writer/console/relationship_writer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/spdx_tools/spdx3/writer/console/relationship_writer.py b/src/spdx_tools/spdx3/writer/console/relationship_writer.py index 1a8b16a3e..9f61e5214 100644 --- a/src/spdx_tools/spdx3/writer/console/relationship_writer.py +++ b/src/spdx_tools/spdx3/writer/console/relationship_writer.py @@ -12,5 +12,5 @@ def write_relationship(relationship: Relationship, text_output: TextIO, heading: if heading: text_output.write("## Relationship\n") write_element_properties(relationship, text_output) - for property_name in relationship.__annotations__.keys(): + for property_name in Relationship.__annotations__.keys(): write_value(property_name, getattr(relationship, property_name), text_output) From ed5d515a47370eb98c2941d2ca4a04f2093e5704 Mon Sep 17 00:00:00 2001 From: Arthit Suriyawongkul Date: Tue, 26 Nov 2024 13:00:07 +0000 Subject: [PATCH 09/23] Add return type Document for parse_file Signed-off-by: Arthit Suriyawongkul --- src/spdx_tools/spdx/parser/parse_anything.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/spdx_tools/spdx/parser/parse_anything.py b/src/spdx_tools/spdx/parser/parse_anything.py index ff41e2b77..b47dc50dc 100644 --- a/src/spdx_tools/spdx/parser/parse_anything.py +++ b/src/spdx_tools/spdx/parser/parse_anything.py @@ -12,6 +12,7 @@ import logging from spdx_tools.spdx.formats import FileFormat, file_name_to_format +from spdx_tools.spdx.model import Document from spdx_tools.spdx.parser.json import json_parser from spdx_tools.spdx.parser.rdf import rdf_parser from spdx_tools.spdx.parser.tagvalue import tagvalue_parser @@ -19,7 +20,7 @@ from spdx_tools.spdx.parser.yaml import yaml_parser -def parse_file(file_name: str, encoding: str = "utf-8"): +def parse_file(file_name: str, encoding: str = "utf-8") -> Document: if encoding != "utf-8": logging.warning( "It's recommended to use the UTF-8 encoding for any SPDX file. Consider changing the encoding of the file." From 7636b1b686f5b7541cfa7d67b27461c7b23e0a1e Mon Sep 17 00:00:00 2001 From: Arthit Suriyawongkul Date: Wed, 23 Apr 2025 20:37:36 +0100 Subject: [PATCH 10/23] Use import.resources instead of __file__ Potentially fix #257 and allow zip-safe = true in pyproject.toml Signed-off-by: Arthit Suriyawongkul --- .../spdx3/writer/json_ld/json_ld_writer.py | 12 +++++++---- .../spdx3/writer/json_ld/owl_to_context.py | 9 +++++--- tests/spdx/test_cli.py | 21 ++++++++++--------- .../writer/json_ld/test_json_ld_writer.py | 5 +++-- 4 files changed, 28 insertions(+), 19 deletions(-) diff --git a/src/spdx_tools/spdx3/writer/json_ld/json_ld_writer.py b/src/spdx_tools/spdx3/writer/json_ld/json_ld_writer.py index 69a4d763c..3c3e0819a 100644 --- a/src/spdx_tools/spdx3/writer/json_ld/json_ld_writer.py +++ b/src/spdx_tools/spdx3/writer/json_ld/json_ld_writer.py @@ -2,20 +2,24 @@ # # SPDX-License-Identifier: Apache-2.0 import json -import os +from importlib import resources from spdx_tools.spdx3.payload import Payload -from spdx_tools.spdx3.writer.json_ld.json_ld_converter import convert_payload_to_json_ld_list_of_elements +from spdx_tools.spdx3.writer.json_ld.json_ld_converter import ( + convert_payload_to_json_ld_list_of_elements, +) def write_payload(payload: Payload, file_name: str): element_list = convert_payload_to_json_ld_list_of_elements(payload) # this will be obsolete as soon as the context is publicly available under some URI - with open(os.path.join(os.path.dirname(__file__), "context.json"), "r") as infile: + # Note: 3.0.1 context is now available at + # https://spdx.org/rdf/3.0.1/spdx-context.jsonld + with resources.files("spdx_tools.spdx3.writer.json_ld").joinpath("context.json").open("r") as infile: context = json.load(infile) complete_dict = {"@context": context, "@graph": element_list} - with open(file_name + ".jsonld", "w") as out: + with open(file_name + ".jsonld", "w", encoding="utf-8") as out: json.dump(complete_dict, out, indent=2) diff --git a/src/spdx_tools/spdx3/writer/json_ld/owl_to_context.py b/src/spdx_tools/spdx3/writer/json_ld/owl_to_context.py index f6bc7c35a..cdeb3144e 100644 --- a/src/spdx_tools/spdx3/writer/json_ld/owl_to_context.py +++ b/src/spdx_tools/spdx3/writer/json_ld/owl_to_context.py @@ -2,7 +2,7 @@ # # SPDX-License-Identifier: Apache-2.0 import json -import os.path +from importlib import resources # current workflow: markdown files + spec_parser -> model.ttl -> convert to json_ld: SPDX_OWL.json -> # use the function below to generate context.json @@ -108,8 +108,11 @@ def convert_spdx_owl_to_jsonld_context(spdx_owl: str = "SPDX_OWL.json"): else: print(f"unknown node_type: {node_type}") - with open(os.path.join(os.path.dirname(__file__), "context.json"), "w") as infile: - json.dump(context_dict, infile) + with resources.as_file( + resources.files("spdx_tools.spdx3.writer.json_ld").joinpath("context.json") + ) as context_path: + with open(context_path, "w", encoding="utf-8") as outfile: + json.dump(context_dict, outfile) if __name__ == "__main__": diff --git a/tests/spdx/test_cli.py b/tests/spdx/test_cli.py index 0019cf3a3..52ea85cf5 100644 --- a/tests/spdx/test_cli.py +++ b/tests/spdx/test_cli.py @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: 2023 spdx contributors # # SPDX-License-Identifier: Apache-2.0 -import os +from importlib import resources import pytest from click.testing import CliRunner @@ -12,16 +12,16 @@ @pytest.mark.parametrize( "options", [ - ("--infile", os.path.join(os.path.dirname(__file__), "data/SPDXJSONExample-v2.3.spdx.json")), - ("-i", os.path.join(os.path.dirname(__file__), "data/SPDXJSONExample-v2.3.spdx.json"), "--novalidation"), + ("--infile", str(resources.files("tests.spdx.data").joinpath("SPDXJSONExample-v2.3.spdx.json"))), + ("-i", str(resources.files("tests.spdx.data").joinpath("SPDXJSONExample-v2.3.spdx.json")), "--novalidation"), ( "-i", - os.path.join(os.path.dirname(__file__), "data/SPDXJSONExample-v2.3.spdx.json"), + str(resources.files("tests.spdx.data").joinpath("SPDXJSONExample-v2.3.spdx.json")), "--novalidation", "--version", "SPDX-2.3", ), - ("-i", os.path.join(os.path.dirname(__file__), "data/SPDXJSONExample-v2.3.spdx.json"), "-o", "-"), + ("-i", str(resources.files("tests.spdx.data").joinpath("SPDXJSONExample-v2.3.spdx.json")), "-o", "-"), ], ) def test_cli_with_system_exit_code_0(options): @@ -37,9 +37,10 @@ def test_cli_with_system_exit_code_0(options): [ ( "-i", - os.path.join( - os.path.dirname(__file__), - "data/invalid/spdx-trivy-vmware_log-intelligence-fluentd-sha256_086af034f561f343f633be9d9f9e95f65ae6c61b8ddb2c6755ef5bb25b40f53a.json", # noqa: E501 + str( + resources.files("tests.spdx.data.invalid").joinpath( + "spdx-trivy-vmware_log-intelligence-fluentd-sha256_086af034f561f343f633be9d9f9e95f65ae6c61b8ddb2c6755ef5bb25b40f53a.json" + ) ), ), ("-i", "non_existent_file.spdx"), @@ -57,8 +58,8 @@ def test_cli_with_system_exit_code_1(options): "options", [ (), - ("-i", os.path.join(os.path.dirname(__file__), "data/SPDXJSONExample-v2.3.spdx.json"), "--version"), - ("-i", os.path.join(os.path.dirname(__file__), "data/SPDXJSONExample-v2.3.spdx.json"), "-o"), + ("-i", str(resources.files("tests.spdx.data").joinpath("SPDXJSONExample-v2.3.spdx.json")), "--version"), + ("-i", str(resources.files("tests.spdx.data").joinpath("SPDXJSONExample-v2.3.spdx.json")), "-o"), ], ) def test_cli_with_system_exit_code_2(options): diff --git a/tests/spdx3/writer/json_ld/test_json_ld_writer.py b/tests/spdx3/writer/json_ld/test_json_ld_writer.py index f46469b1f..4718f3e6e 100644 --- a/tests/spdx3/writer/json_ld/test_json_ld_writer.py +++ b/tests/spdx3/writer/json_ld/test_json_ld_writer.py @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: 2023 spdx contributors # # SPDX-License-Identifier: Apache-2.0 -import os +from importlib import resources from spdx_tools.spdx3.bump_from_spdx2.spdx_document import bump_spdx_document from spdx_tools.spdx3.payload import Payload @@ -15,4 +15,5 @@ def test_json_writer(): payload: Payload = bump_spdx_document(spdx2_document) # this currently generates an actual file to look at, this should be changed to a temp file later - write_payload(payload, os.path.join(os.path.dirname(__file__), "../../../SPDX3_jsonld_test")) + with resources.as_file(resources.files("tests.spdx3.writer.json_ld").joinpath("SPDX3_jsonld_test")) as output_file: + write_payload(payload, str(output_file)) From 0ce225c41bd89063b75f22bb32f30a7fdb4d6b00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Fri, 9 Jan 2026 16:16:18 +0100 Subject: [PATCH 11/23] linter fix: reduce line length MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- tests/spdx/test_cli.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/spdx/test_cli.py b/tests/spdx/test_cli.py index 52ea85cf5..d3b0bd4f3 100644 --- a/tests/spdx/test_cli.py +++ b/tests/spdx/test_cli.py @@ -39,7 +39,8 @@ def test_cli_with_system_exit_code_0(options): "-i", str( resources.files("tests.spdx.data.invalid").joinpath( - "spdx-trivy-vmware_log-intelligence-fluentd-sha256_086af034f561f343f633be9d9f9e95f65ae6c61b8ddb2c6755ef5bb25b40f53a.json" + "spdx-trivy-vmware_log-intelligence-fluentd-" + "sha256_086af034f561f343f633be9d9f9e95f65ae6c61b8ddb2c6755ef5bb25b40f53a.json" ) ), ), From f37a321c7cddd190fdb89486fd0f96499eeca0e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Fri, 9 Jan 2026 16:43:56 +0100 Subject: [PATCH 12/23] update actions/deploy-pages to v4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * This hopefully fixes the GitHub actions workflow that currently doesn't find the correct url. Signed-off-by: Armin Tänzer --- .github/workflows/docs.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index be0d749b1..46dc4157a 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -7,6 +7,7 @@ on: push: branches: - main + workflow_dispatch: permissions: contents: read @@ -44,4 +45,4 @@ jobs: steps: - id: deployment name: Deploy docs to GitHub pages - uses: actions/deploy-pages@v2 + uses: actions/deploy-pages@v4 From fa5a3348e0ca2da3a5bd84a8d30fa77226886c4e Mon Sep 17 00:00:00 2001 From: Arthit Suriyawongkul Date: Tue, 8 Oct 2024 13:43:46 +0200 Subject: [PATCH 13/23] Fix Markdown warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove trailing whitespaces - Ensure one blank line before and after headings/lists - Enforce one H1 heading in a document, the rest will be H2, H3, etc. - Standardize the use of bullet markup (using all `-`, instead of mixing `-` and `*`) Signed-off-by: Arthit Suriyawongkul Signed-off-by: Armin Tänzer --- CONTRIBUTING.md | 31 +++++--- DOCUMENTATION.md | 22 +++++- README.md | 196 +++++++++++++++++++++++++---------------------- 3 files changed, 143 insertions(+), 106 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4c4a04341..88638e70e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,7 +15,7 @@ intention prior to creating a patch. ## Development process -We use the GitHub flow that is described here: https://guides.github.com/introduction/flow/ +We use the GitHub flow that is described here: Here's the process to make changes to the codebase: @@ -30,15 +30,19 @@ Here's the process to make changes to the codebase: and optionally follow the further steps described to sync your fork and the original repository. 4. Create a new branch in your fork and set up environment: + ```sh git checkout -b fix-or-improve-something python -m venv ./venv ./venv/bin/activate pip install -e ".[development]" ``` - Note: By using the group `[development]` for the installation, all dependencies (including optional ones) will be - installed. This way we make sure that all tests are executed. + + Note: By using the group `[development]` for the installation, all dependencies (including optional ones) will be + installed. This way we make sure that all tests are executed. + 5. Make some changes and commit them to the branch: + ```sh git commit --signoff -m 'description of my changes' ``` @@ -49,27 +53,33 @@ Here's the process to make changes to the codebase: of [the Developer Certificate of Origin](https://developercertificate.org/). Git has utilities for signing off on commits: `git commit -s` or `--signoff` signs a current commit, and `git rebase --signoff ` retroactively signs a range of past commits. + 6. Test your changes: + ```sh pytest -vvs # in the repo root ``` -7. Check your code style. When opening a pull request, your changes will automatically be checked with `isort`, `black` - and `flake8` to make sure your changes fit with the rest of the code style. +7. Check your code style. When opening a pull request, your changes will automatically be checked with `isort`, `black` + and `flake8` to make sure your changes fit with the rest of the code style. + ```sh # run the following commands in the repo root - isort src tests + isort src tests black src tests - flake8 src tests + flake8 src tests ``` - `black` and `isort` will automatically format the code and sort the imports. The configuration for these linters + + `black` and `isort` will automatically format the code and sort the imports. The configuration for these linters can be found in the `pyproject.toml`. `flake8` lists all problems found which then need to be resolved manually. The configuration for the linter can be found in the `.flake8` file. 8. Push the branch to your fork on GitHub: + ```sh git push origin fix-or-improve-something ``` + 9. Make a pull request on GitHub. 10. Continue making more changes and commits on the branch, with `git commit --signoff` and `git push`. 11. When done, write a comment on the PR asking for a code review. @@ -77,6 +87,7 @@ Here's the process to make changes to the codebase: possible, or with `squash`. 13. The temporary branch on GitHub should be deleted (there is a button for deleting it). 14. Delete the local branch as well: + ```sh git checkout master git pull -p @@ -84,11 +95,11 @@ Here's the process to make changes to the codebase: git branch -d fix-or-improve-something ``` -# How to run tests +## How to run tests The tests framework is using pytest: -``` +```sh pip install pytest pytest -vvs ``` diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md index f8c7e274b..5119dd467 100644 --- a/DOCUMENTATION.md +++ b/DOCUMENTATION.md @@ -1,12 +1,15 @@ # Code architecture documentation ## Package Overview + Beneath the top-level package `spdx_tools` you will find three sub-packages: + - `spdx`, which contains the code to create, parse, write and validate SPDX documents of versions 2.2 and 2.3 - `spdx3`, which will contain the same feature set for versions 3.x once they are released - `common`, which contains code that is shared between the different versions, such as type-checking and `spdx_licensing`. ## `spdx` + The `spdx` package contains the code dealing with SPDX-2 documents. The subpackages serve the purpose to divide the code into logically independent chunks. Shared code can be found in the top-level modules here. `model`, `parser`, `validation` and `writer` constitute the four main components of this library and are further described below. @@ -14,9 +17,11 @@ The subpackages serve the purpose to divide the code into logically independent `jsonschema` and `rdfschema` contain code specific to the corresponding serialization format. ### `model` + The internal data model closely follows the [official SPDX-2.3 specification](https://spdx.github.io/spdx-spec/v2.3/). Entrypoint to the model is the `Document` class, which has the following attributes: + - `creation_info`: a single instance of the `CreationInfo` class - `packages`: a list of `Package` objects - `files`: a list of `File` objects @@ -35,6 +40,7 @@ A custom extension of the `@dataclass` annotation is used that is called `@datac Apart from all the usual `dataclass` functionality, this implements fields of a class as properties with their own getter and setter methods. This is used in particular to implement type checking when properties are set. Source of truth for these checks are the attribute definitions at the start of the respective class that must specify the correct type hint. + The `beartype` library is used to check type conformity (`typeguard` was used in the past but has been replaced since due to performance issues). In case of a type mismatch a `TypeError` is raised. To ensure that all possible type errors are found during the construction of an object, a custom `__init__()` that calls `check_types_and_set_values()` is part of every class. @@ -43,26 +49,31 @@ This function tries to set all values provided by the constructor and collects a For the SPDX values `NONE` and `NOASSERTION` the classes `SpdxNone` and `SpdxNoAssertion` are used, respectively. Both can be instantiated without any arguments. ### `parser` + The parsing and writing modules are split into subpackages according to the serialization formats: `json`, `yaml`, `xml`, `tagvalue` and `rdf`. As the first three share the same tree structure that can be parsed into a dictionary, their shared logic is contained in the `jsonlikedict` package. One overarching concept of all parsers is the goal of dealing with parsing errors (like faulty types or missing mandatory fields) as long as possible before failing. Thus, the `SPDXParsingError` that is finally raised collects as much information as possible about all parsing errors that occurred. #### `tagvalue` + Since Tag-Value is an SPDX-specific format, there exist no readily available parsers for it. -This library implements its own deserialization code using the `ply` library's `lex` module for lexing and the `yacc` module for parsing. +This library implements its own deserialization code using the `ply` library's `lex` module for lexing and the `yacc` module for parsing. #### `rdf` + The `rdflib` library is used to deserialize RDF graphs from XML format. -The graph is then being parsed and translated into the internal data model. +The graph is then being parsed and translated into the internal data model. #### `json`, `yaml`, `xml` + In a first step, all three of JSON, YAML and XML formats are deserialized into a dictionary representing their tree structure. This is achieved via the `json`, `yaml` and `xmltodict` packages, respectively. Special note has to be taken in the XML case which does not support lists and numbers. The logic concerning the translation from these dicts to the internal data model can be found in the `jsonlikedict` package. ### `writer` + For serialization purposes, only non-null fields are written out. All writers expect a valid SPDX document from the internal model as input. To ensure this is actually the case, the standard behaviour of every writer function is to call validation before the writing process. @@ -71,18 +82,21 @@ Also by default, all list properties in the model are scanned for duplicates whi This can be disabled by setting the `drop_duplicates` boolean to false. #### `tagvalue` + The ordering of the tags follows the [example in the official specification](https://github.com/spdx/spdx-spec/blob/development/v2.3.1/examples/SPDXTagExample-v2.3.spdx). #### `rdf` + The RDF graph is constructed from the internal data model and serialized to XML format afterward, using the `rdflib` library. #### `json`, `yaml`, `xml` + As all three of JSON, YAML and XML formats share the same tree structure, the first step is to generate the dictionary representing that tree. This is achieved by the `DocumentConverter` class in the `jsonschema` package. Subsequently, the dictionary is serialized using the `json`, `yaml` and `xmltodict` packages, respectively. - ### `validation` + The `validation` package takes care of all nonconformities with the SPDX specification that are not due to incorrect typing. This mainly includes checks for correctly formatted strings or the actual existence of references SPDXIDs. Entrypoint is the `document_validator` module with the `validate_full_spdx_document()` function. @@ -93,6 +107,7 @@ Validation and reference checking of SPDXIDs (and possibly external document ref For the validation of license expressions we utilise the `license-expression` library's `validate` and `parse` functions, which take care of checking license symbols against the [SPDX license list](https://spdx.org/licenses/). Invalidities are captured in instances of a custom `ValidationMessage` class. This has two attributes: + - `validation_message` is a string that describes the actual problem - `validation_context` is a `ValidationContext` object that helps to pinpoint the source of the problem by providing the faulty element's SPDXID (if it has one), the parent SPDXID (if that is known), the element's type and finally the full element itself. It is left open to the implementer which of this information to use in the following evaluation of the validation process. @@ -101,6 +116,7 @@ Every validation function returns a list of `ValidationMessage` objects, which a That is, if an empty list is returned, the document is valid. ## `spdx3` + Due to the SPDX-3 model still being in development, this package is still a work in progress. However, as the basic building blocks of parsing, writing, creation and validation are still important in the new version, the `spdx3` package is planned to be structured similarly to the `spdx` package. diff --git a/README.md b/README.md index e089bd138..0dbe4514c 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,9 @@ CI status (Linux, macOS and Windows): [![Install and Test][1]][2] [1]: https://github.com/spdx/tools-python/actions/workflows/install_and_test.yml/badge.svg - [2]: https://github.com/spdx/tools-python/actions/workflows/install_and_test.yml - -# Breaking changes v0.7 -> v0.8 +## Breaking changes v0.7 -> v0.8 Please be aware that the upcoming 0.8 release has undergone a significant refactoring in preparation for the upcoming SPDX v3.0 release, leading to breaking changes in the API. @@ -15,126 +13,137 @@ Please refer to the [migration guide](https://github.com/spdx/tools-python/wiki/ to update your existing code. The main features of v0.8 are: + - full validation of SPDX documents against the v2.2 and v2.3 specification - support for SPDX's RDF format with all v2.3 features -- experimental support for the upcoming SPDX v3 specification. Note, however, that support is neither complete nor - stable at this point, as the spec is still evolving. SPDX3-related code is contained in a separate subpackage "spdx3" +- experimental support for the upcoming SPDX v3 specification. Note, however, that support is neither complete nor + stable at this point, as the spec is still evolving. SPDX3-related code is contained in a separate subpackage "spdx3" and its use is optional. We do not recommend using it in production code yet. Note that v0.8 only supports **writing**, not **reading** SPDX 3.0 documents. See [#760](https://github.com/spdx/tools-python/issues/760) for details. -# Information +## Information This library implements SPDX parsers, convertors, validators and handlers in Python. -- Home: https://github.com/spdx/tools-python -- Issues: https://github.com/spdx/tools-python/issues -- PyPI: https://pypi.python.org/pypi/spdx-tools -- Browse the API: https://spdx.github.io/tools-python +- Home: +- Issues: +- PyPI: +- Browse the API: -Important updates regarding this library are shared via the SPDX tech mailing list: https://lists.spdx.org/g/Spdx-tech. +Important updates regarding this library are shared via the SPDX tech mailing list: . - -# License +## License [Apache-2.0](LICENSE) -# Features +## Features -* API to create and manipulate SPDX v2.2 and v2.3 documents -* Parse, convert, create and validate SPDX files -* supported formats: Tag/Value, RDF, JSON, YAML, XML -* visualize the structure of a SPDX document by creating an `AGraph`. Note: This is an optional feature and requires +- API to create and manipulate SPDX v2.2 and v2.3 documents +- Parse, convert, create and validate SPDX files +- supported formats: Tag/Value, RDF, JSON, YAML, XML +- visualize the structure of a SPDX document by creating an `AGraph`. Note: This is an optional feature and requires additional installation of optional dependencies ## Experimental support for SPDX 3.0 -* Create v3.0 elements and payloads -* Convert v2.2/v2.3 documents to v3.0 -* Serialize to JSON-LD -See [Quickstart to SPDX 3.0](#quickstart-to-spdx-30) below. -The implementation is based on the descriptive markdown files in the repository https://github.com/spdx/spdx-3-model (latest commit: a5372a3c145dbdfc1381fc1f791c68889aafc7ff). +- Create v3.0 elements and payloads +- Convert v2.2/v2.3 documents to v3.0 +- Serialize to JSON-LD +See [Quickstart to SPDX 3.0](#quickstart-to-spdx-30) below. +The implementation is based on the descriptive Markdown files in the repository (latest commit: a5372a3c145dbdfc1381fc1f791c68889aafc7ff). -# Installation +## Installation As always you should work in a virtualenv (venv). You can install a local clone -of this repo with `yourenv/bin/pip install .` or install it from PyPI +of this repo with `yourenv/bin/pip install .` or install it from PyPI (check for the [newest release](https://pypi.org/project/spdx-tools/#history) and install it like `yourenv/bin/pip install spdx-tools==0.8.0a2`). Note that on Windows it would be `Scripts` instead of `bin`. -# How to use +## How to use -## Command-line usage +### Command-line usage 1. **PARSING/VALIDATING** (for parsing any format): -* Use `pyspdxtools -i ` where `` is the location of the file. The input format is inferred automatically from the file ending. + - Use `pyspdxtools -i ` where `` is the location of the file. The input format is inferred automatically from the file ending. -* If you are using a source distribution, try running: - `pyspdxtools -i tests/data/SPDXJSONExample-v2.3.spdx.json` + - If you are using a source distribution, try running: + `pyspdxtools -i tests/data/SPDXJSONExample-v2.3.spdx.json` 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 input and output formats are inferred automatically from the file endings. + - Use `pyspdxtools -i -o ` where `` is the location of the file to be converted + and `` is the location of the output file. The input and output formats are inferred automatically from the file endings. -* If you are using a source distribution, try running: - `pyspdxtools -i tests/data/SPDXJSONExample-v2.3.spdx.json -o output.tag` + - If you are using a source distribution, try running: + `pyspdxtools -i tests/data/SPDXJSONExample-v2.3.spdx.json -o output.tag` -* If you want to skip the validation process, provide the `--novalidation` flag, like so: - `pyspdxtools -i tests/data/SPDXJSONExample-v2.3.spdx.json -o output.tag --novalidation` + - If you want to skip the validation process, provide the `--novalidation` flag, like so: + `pyspdxtools -i tests/data/SPDXJSONExample-v2.3.spdx.json -o output.tag --novalidation` (use this with caution: note that undetected invalid documents may lead to unexpected behavior of the tool) - -* For help use `pyspdxtools --help` + + - For help use `pyspdxtools --help` 3. **GRAPH GENERATION** (optional feature) -* This feature generates a graph representing all elements in the SPDX document and their connections based on the provided - relationships. The graph can be rendered to a picture. Below is an example for the file `tests/data/SPDXJSONExample-v2.3.spdx.json`: -![SPDXJSONExample-v2.3.spdx.png](assets/SPDXJSONExample-v2.3.spdx.png) -* Make sure you install the optional dependencies `networkx` and `pygraphviz`. To do so run `pip install ".[graph_generation]"`. -* Use `pyspdxtools -i --graph -o ` where `` is an output file name with valid format for `pygraphviz` (check - the documentation [here](https://pygraphviz.github.io/documentation/stable/reference/agraph.html#pygraphviz.AGraph.draw)). -* If you are using a source distribution, try running - `pyspdxtools -i tests/data/SPDXJSONExample-v2.3.spdx.json --graph -o SPDXJSONExample-v2.3.spdx.png` to generate - a png with an overview of the structure of the example file. - -## Library usage + - This feature generates a graph representing all elements in the SPDX document and their connections based on the provided + relationships. The graph can be rendered to a picture. Below is an example for the file `tests/data/SPDXJSONExample-v2.3.spdx.json`: + ![SPDXJSONExample-v2.3.spdx.png](assets/SPDXJSONExample-v2.3.spdx.png) + + - Make sure you install the optional dependencies `networkx` and `pygraphviz`. To do so run `pip install ".[graph_generation]"`. + - Use `pyspdxtools -i --graph -o ` where `` is an output file name with valid format for `pygraphviz` (check + the documentation [here](https://pygraphviz.github.io/documentation/stable/reference/agraph.html#pygraphviz.AGraph.draw)). + - If you are using a source distribution, try running + `pyspdxtools -i tests/data/SPDXJSONExample-v2.3.spdx.json --graph -o SPDXJSONExample-v2.3.spdx.png` to generate + a png with an overview of the structure of the example file. + +### Library usage + 1. **DATA MODEL** - * The `spdx_tools.spdx.model` package constitutes the internal SPDX v2.3 data model (v2.2 is simply a subset of this). All relevant classes for SPDX document creation are exposed in the `__init__.py` found [here](src%2Fspdx_tools%2Fspdx%2Fmodel%2F__init__.py). - * SPDX objects are implemented via `@dataclass_with_properties`, a custom extension of `@dataclass`. - * Each class starts with a list of its properties and their possible types. When no default value is provided, the property is mandatory and must be set during initialization. - * Using the type hints, type checking is enforced when initializing a new instance or setting/getting a property on an instance + + - The `spdx_tools.spdx.model` package constitutes the internal SPDX v2.3 data model (v2.2 is simply a subset of this). All relevant classes for SPDX document creation are exposed in the `__init__.py` found [here](src%2Fspdx_tools%2Fspdx%2Fmodel%2F__init__.py). + - SPDX objects are implemented via `@dataclass_with_properties`, a custom extension of `@dataclass`. + - Each class starts with a list of its properties and their possible types. When no default value is provided, the property is mandatory and must be set during initialization. + - Using the type hints, type checking is enforced when initializing a new instance or setting/getting a property on an instance (wrong types will raise `ConstructorTypeError` or `TypeError`, respectively). This makes it easy to catch invalid properties early and only construct valid documents. - * Note: in-place manipulations like `list.append(item)` will circumvent the type checking (a `TypeError` will still be raised when reading `list` again). We recommend using `list = list + [item]` instead. - * The main entry point of an SPDX document is the `Document` class from the [document.py](src%2Fspdx_tools%2Fspdx%2Fmodel%2Fdocument.py) module, which links to all other classes. - * For license handling, the [license_expression](https://github.com/nexB/license-expression) library is used. - * Note on `documentDescribes` and `hasFiles`: These fields will be converted to relationships in the internal data model. As they are deprecated, these fields will not be written in the output. + - Note: in-place manipulations like `list.append(item)` will circumvent the type checking (a `TypeError` will still be raised when reading `list` again). We recommend using `list = list + [item]` instead. + - The main entry point of an SPDX document is the `Document` class from the [document.py](src%2Fspdx_tools%2Fspdx%2Fmodel%2Fdocument.py) module, which links to all other classes. + - For license handling, the [license_expression](https://github.com/nexB/license-expression) library is used. + - Note on `documentDescribes` and `hasFiles`: These fields will be converted to relationships in the internal data model. As they are deprecated, these fields will not be written in the output. + 2. **PARSING** - * Use `parse_file(file_name)` from the `parse_anything.py` module to parse an arbitrary file with one of the supported file endings. - * Successful parsing will return a `Document` instance. Unsuccessful parsing will raise `SPDXParsingError` with a list of all encountered problems. + + - Use `parse_file(file_name)` from the `parse_anything.py` module to parse an arbitrary file with one of the supported file endings. + - Successful parsing will return a `Document` instance. Unsuccessful parsing will raise `SPDXParsingError` with a list of all encountered problems. + 3. **VALIDATING** - * Use `validate_full_spdx_document(document)` to validate an instance of the `Document` class. - * This will return a list of `ValidationMessage` objects, each consisting of a String describing the invalidity and a `ValidationContext` to pinpoint the source of the validation error. - * Validation depends on the SPDX version of the document. Note that only versions `SPDX-2.2` and `SPDX-2.3` are supported by this tool. + + - Use `validate_full_spdx_document(document)` to validate an instance of the `Document` class. + - This will return a list of `ValidationMessage` objects, each consisting of a String describing the invalidity and a `ValidationContext` to pinpoint the source of the validation error. + - Validation depends on the SPDX version of the document. Note that only versions `SPDX-2.2` and `SPDX-2.3` are supported by this tool. + 4. **WRITING** - * Use `write_file(document, file_name)` from the `write_anything.py` module to write a `Document` instance to the specified file. + + - Use `write_file(document, file_name)` from the `write_anything.py` module to write a `Document` instance to the specified file. The serialization format is determined from the filename ending. - * Validation is performed per default prior to the writing process, which is cancelled if the document is invalid. You can skip the validation via `write_file(document, file_name, validate=False)`. + - Validation is performed per default prior to the writing process, which is cancelled if the document is invalid. You can skip the validation via `write_file(document, file_name, validate=False)`. Caution: Only valid documents can be serialized reliably; serialization of invalid documents is not supported. -## Example +### Example + Here are some examples of possible use cases to quickly get you started with the spdx-tools. If you want more examples, like how to create an SPDX document from scratch, have a look [at the examples folder](examples). + ```python import logging from license_expression import get_spdx_licensing -from spdx_tools.spdx.model import (Checksum, ChecksumAlgorithm, File, +from spdx_tools.spdx.model import (Checksum, ChecksumAlgorithm, File, FileType, Relationship, RelationshipType) from spdx_tools.spdx.parser.parse_anything import parse_file from spdx_tools.spdx.validation.document_validator import validate_full_spdx_document @@ -149,14 +158,14 @@ document.creation_info.name = "new document name" # define a file and a DESCRIBES relationship between the file and the document checksum = Checksum(ChecksumAlgorithm.SHA1, "71c4025dd9897b364f3ebbb42c484ff43d00791c") -file = File(name="./fileName.py", spdx_id="SPDXRef-File", checksums=[checksum], - file_types=[FileType.TEXT], +file = File(name="./fileName.py", spdx_id="SPDXRef-File", checksums=[checksum], + file_types=[FileType.TEXT], license_concluded=get_spdx_licensing().parse("MIT and GPL-2.0"), license_comment="licenseComment", copyright_text="copyrightText") relationship = Relationship("SPDXRef-DOCUMENT", RelationshipType.DESCRIBES, "SPDXRef-File") -# add the file and the relationship to the document +# add the file and the relationship to the document # (note that we do not use "document.files.append(file)" as that would circumvent the type checking) document.files = document.files + [file] document.relationships = document.relationships + [relationship] @@ -167,51 +176,52 @@ validation_messages = validate_full_spdx_document(document) for validation_message in validation_messages: logging.warning(validation_message.validation_message) -# if there are no validation messages, the document is valid +# if there are no validation messages, the document is valid # and we can safely serialize it without validating again if not validation_messages: write_file(document, "new_spdx_document.rdf", validate=False) ``` -# Quickstart to SPDX 3.0 +## Quickstart to SPDX 3.0 + In contrast to SPDX v2, all elements are now subclasses of the central `Element` class. -This includes packages, files, snippets, relationships, annotations, but also SBOMs, SpdxDocuments, and more. +This includes packages, files, snippets, relationships, annotations, but also SBOMs, SpdxDocuments, and more. For serialization purposes, all Elements that are to be serialized into the same file are collected in a `Payload`. This is just a dictionary that maps each Element's SpdxId to itself. Use the `write_payload()` functions to serialize a payload. -There currently are two options: -* The `spdx_tools.spdx3.writer.json_ld.json_ld_writer` module generates a JSON-LD file of the payload. -* The `spdx_tools.spdx3.writer.console.payload_writer` module prints a debug output to console. Note that this is not an official part of the SPDX specification and will probably be dropped as soon as a better standard emerges. +There currently are two options: + +- The `spdx_tools.spdx3.writer.json_ld.json_ld_writer` module generates a JSON-LD file of the payload. +- The `spdx_tools.spdx3.writer.console.payload_writer` module prints a debug output to console. Note that this is not an official part of the SPDX specification and will probably be dropped as soon as a better standard emerges. You can convert an SPDX v2 document to v3 via the `spdx_tools.spdx3.bump_from_spdx2.spdx_document` module. The `bump_spdx_document()` function will return a payload containing an `SpdxDocument` Element and one Element for each package, file, snippet, relationship, or annotation contained in the v2 document. +## Dependencies -# Dependencies - -* PyYAML: https://pypi.org/project/PyYAML/ for handling YAML. -* xmltodict: https://pypi.org/project/xmltodict/ for handling XML. -* rdflib: https://pypi.python.org/pypi/rdflib/ for handling RDF. -* ply: https://pypi.org/project/ply/ for handling tag-value. -* click: https://pypi.org/project/click/ for creating the CLI interface. -* beartype: https://pypi.org/project/beartype/ for type checking. -* uritools: https://pypi.org/project/uritools/ for validation of URIs. -* license-expression: https://pypi.org/project/license-expression/ for handling SPDX license expressions. +- PyYAML: for handling YAML. +- xmltodict: for handling XML. +- rdflib: for handling RDF. +- ply: for handling tag-value. +- click: for creating the CLI interface. +- beartype: for type checking. +- uritools: for validation of URIs. +- license-expression: for handling SPDX license expressions. -# Support +## Support -* Submit issues, questions or feedback at https://github.com/spdx/tools-python/issues -* Join the chat at https://gitter.im/spdx-org/Lobby -* Join the discussion on https://lists.spdx.org/g/spdx-tech and - https://spdx.dev/participate/tech/ +- Submit issues, questions or feedback at +- Join the chat at +- Join the discussion on and + -# Contributing +## Contributing Contributions are very welcome! See [CONTRIBUTING.md](./CONTRIBUTING.md) for instructions on how to contribute to the codebase. -# History +## History This is the result of an initial GSoC contribution by @[ah450](https://github.com/ah450) -(or https://github.com/a-h-i) and is maintained by a community of SPDX adopters and enthusiasts. +(or ) and is maintained by a community of SPDX adopters and enthusiasts. In order to prepare for the release of SPDX v3.0, the repository has undergone a major refactoring during the time from 11/2022 to 07/2023. From 26fca3c6d78f52c7a5d2a1287e5ec8a235ae3b74 Mon Sep 17 00:00:00 2001 From: Arthit Suriyawongkul Date: Tue, 8 Oct 2024 19:44:31 +0700 Subject: [PATCH 14/23] Fix process.md Signed-off-by: Arthit Suriyawongkul --- .../spdx3/writer/json_ld/process.md | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/spdx_tools/spdx3/writer/json_ld/process.md b/src/spdx_tools/spdx3/writer/json_ld/process.md index 7d04d5ccd..1968e6e48 100644 --- a/src/spdx_tools/spdx3/writer/json_ld/process.md +++ b/src/spdx_tools/spdx3/writer/json_ld/process.md @@ -1,27 +1,29 @@ -### Workflow +# Workflow Process to produce context file and a serialization example: -1. Run -``` -spec-parser --gen-md --gen-refs --gen-rdf ../spdx-3-model/model -``` -- spdx-3-model (commit: 6cb4316, last commit where spec-parser is able to run)
-- spec-parser (main with commits from PR 44, 45) +1. Run -2. Convert the generated `spec-parser/md_generated/model.ttl` to a json-ld file using https://frogcat.github.io/ttl2jsonld/demo/. + ```sh + spec-parser --gen-md --gen-refs --gen-rdf ../spdx-3-model/model + ``` + + - spdx-3-model (commit: 6cb4316, last commit where spec-parser is able to run) + - spec-parser (main with commits from PR 44, 45) + +2. Convert the generated `spec-parser/md_generated/model.ttl` to a json-ld file using . 3. Convert owl to context using `convert_spdx_owl_to_jsonld_context("SPDX_OWL.json")`. 4. Place the generated `context.json` in `spdx_tools/spdx3/writer/jsonld/`. 5. To generate the jsonld from the testfile run -``` -pyspdxtools3 -i ./tests/spdx/data/SPDXJSONExample-v2.3.spdx.json -o example_with_context -``` + ```sh + pyspdxtools3 -i ./tests/spdx/data/SPDXJSONExample-v2.3.spdx.json -o example_with_context + ``` -### Manually +## Manually +## Known limitations -### Known limitations - Validation of enums does not work - Additional keys seem to be ignored in validation -- inherited properties aren't validated +- Inherited properties aren't validated From 661f2220fef28272e88e85e40829ac954eb4fbe2 Mon Sep 17 00:00:00 2001 From: Arthit Suriyawongkul Date: Tue, 8 Oct 2024 21:47:00 +0700 Subject: [PATCH 15/23] Fix tests/spdx/data path Signed-off-by: Arthit Suriyawongkul --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 0dbe4514c..776135c98 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ The implementation is based on the descriptive Markdown files in the repository As always you should work in a virtualenv (venv). You can install a local clone of this repo with `yourenv/bin/pip install .` or install it from PyPI (check for the [newest release](https://pypi.org/project/spdx-tools/#history) and install it like -`yourenv/bin/pip install spdx-tools==0.8.0a2`). Note that on Windows it would be `Scripts` +`yourenv/bin/pip install spdx-tools==0.8.3`). Note that on Windows it would be `Scripts` instead of `bin`. ## How to use @@ -72,7 +72,7 @@ instead of `bin`. - Use `pyspdxtools -i ` where `` is the location of the file. The input format is inferred automatically from the file ending. - If you are using a source distribution, try running: - `pyspdxtools -i tests/data/SPDXJSONExample-v2.3.spdx.json` + `pyspdxtools -i tests/spdx/data/SPDXJSONExample-v2.3.spdx.json` 2. **CONVERTING** (for converting one format to another): @@ -80,10 +80,10 @@ instead of `bin`. and `` is the location of the output file. The input and output formats are inferred automatically from the file endings. - If you are using a source distribution, try running: - `pyspdxtools -i tests/data/SPDXJSONExample-v2.3.spdx.json -o output.tag` + `pyspdxtools -i tests/spdx/data/SPDXJSONExample-v2.3.spdx.json -o output.tag` - If you want to skip the validation process, provide the `--novalidation` flag, like so: - `pyspdxtools -i tests/data/SPDXJSONExample-v2.3.spdx.json -o output.tag --novalidation` + `pyspdxtools -i tests/spdx/data/SPDXJSONExample-v2.3.spdx.json -o output.tag --novalidation` (use this with caution: note that undetected invalid documents may lead to unexpected behavior of the tool) - For help use `pyspdxtools --help` @@ -91,14 +91,14 @@ instead of `bin`. 3. **GRAPH GENERATION** (optional feature) - This feature generates a graph representing all elements in the SPDX document and their connections based on the provided - relationships. The graph can be rendered to a picture. Below is an example for the file `tests/data/SPDXJSONExample-v2.3.spdx.json`: + relationships. The graph can be rendered to a picture. Below is an example for the file `tests/spdx/data/SPDXJSONExample-v2.3.spdx.json`: ![SPDXJSONExample-v2.3.spdx.png](assets/SPDXJSONExample-v2.3.spdx.png) - Make sure you install the optional dependencies `networkx` and `pygraphviz`. To do so run `pip install ".[graph_generation]"`. - Use `pyspdxtools -i --graph -o ` where `` is an output file name with valid format for `pygraphviz` (check the documentation [here](https://pygraphviz.github.io/documentation/stable/reference/agraph.html#pygraphviz.AGraph.draw)). - If you are using a source distribution, try running - `pyspdxtools -i tests/data/SPDXJSONExample-v2.3.spdx.json --graph -o SPDXJSONExample-v2.3.spdx.png` to generate + `pyspdxtools -i tests/spdx/data/SPDXJSONExample-v2.3.spdx.json --graph -o SPDXJSONExample-v2.3.spdx.png` to generate a png with an overview of the structure of the example file. ### Library usage From 61f771356572de7c058b025440544bcf9b27bdc8 Mon Sep 17 00:00:00 2001 From: Arthit Suriyawongkul Date: Thu, 10 Oct 2024 15:13:44 +0700 Subject: [PATCH 16/23] Update spec-parser parameters Signed-off-by: Arthit Suriyawongkul --- src/spdx_tools/spdx3/writer/json_ld/process.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/spdx_tools/spdx3/writer/json_ld/process.md b/src/spdx_tools/spdx3/writer/json_ld/process.md index 1968e6e48..485df8805 100644 --- a/src/spdx_tools/spdx3/writer/json_ld/process.md +++ b/src/spdx_tools/spdx3/writer/json_ld/process.md @@ -5,16 +5,17 @@ Process to produce context file and a serialization example: 1. Run ```sh - spec-parser --gen-md --gen-refs --gen-rdf ../spdx-3-model/model + python spec-parser/main.py spdx-3-model/model parser_output ``` - - spdx-3-model (commit: 6cb4316, last commit where spec-parser is able to run) - - spec-parser (main with commits from PR 44, 45) + - spdx-3-model (main; where v3.0.1 development happens) + - spec-parser (main) -2. Convert the generated `spec-parser/md_generated/model.ttl` to a json-ld file using . -3. Convert owl to context using `convert_spdx_owl_to_jsonld_context("SPDX_OWL.json")`. -4. Place the generated `context.json` in `spdx_tools/spdx3/writer/jsonld/`. -5. To generate the jsonld from the testfile run +2. Convert the generated `parser_output/rdf/spdx-model.ttl` to a JSON-LD file + using . +3. Convert OWL to context using `owl_to_context.py`. +4. Place the generated `context.json` in `src/spdx_tools/spdx3/writer/json_ld/`. +5. To generate the JSON-LD from the test file, run: ```sh pyspdxtools3 -i ./tests/spdx/data/SPDXJSONExample-v2.3.spdx.json -o example_with_context From 93a5232c8dc80c52fc5c754521567e46f3c0ca43 Mon Sep 17 00:00:00 2001 From: Arthit Suriyawongkul Date: Thu, 10 Oct 2024 19:03:39 +0700 Subject: [PATCH 17/23] Add link to official serialization doc Signed-off-by: Arthit Suriyawongkul --- src/spdx_tools/spdx3/writer/json_ld/process.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/spdx_tools/spdx3/writer/json_ld/process.md b/src/spdx_tools/spdx3/writer/json_ld/process.md index 485df8805..02dc7b715 100644 --- a/src/spdx_tools/spdx3/writer/json_ld/process.md +++ b/src/spdx_tools/spdx3/writer/json_ld/process.md @@ -1,5 +1,10 @@ # Workflow +Official SPDX v3.0 serialization documentation and context file +are available at: + +## Manually generate context file + Process to produce context file and a serialization example: 1. Run @@ -21,8 +26,6 @@ Process to produce context file and a serialization example: pyspdxtools3 -i ./tests/spdx/data/SPDXJSONExample-v2.3.spdx.json -o example_with_context ``` -## Manually - ## Known limitations - Validation of enums does not work From 11db2cf71ae4e02ae932b03bdfac328aecae499d Mon Sep 17 00:00:00 2001 From: Arthit Suriyawongkul Date: Thu, 10 Oct 2024 19:16:19 +0700 Subject: [PATCH 18/23] Add link to SPDX 3.0 model Signed-off-by: Arthit Suriyawongkul --- CHANGELOG.md | 4 ++-- README.md | 16 +++++++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f17ee022c..56126e898 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -141,12 +141,12 @@ Starting a Changelog. * Dropped Python 2 support. Python >= 3.6 is now required. * Added `pyspdxtools_convertor` and `pyspdxtools_parser` CLI scripts. See [the readme](README.md) for usage instructions. -* Updated the tools to support SPDX versions up to 2.3 and to conform with the specification. Apart from many bugfixes +* Updated the tools to support SPDX versions up to 2.3 and to conform with the specification. Apart from many bugfixes and new properties, some of the more significant changes include: * Support for multiple packages per document * Support for multiple checksums for packages and files * Support for files outside a package -* **Note**: Validation was updated to follow the 2.3 specification. Since there is currently no support for +* **Note**: Validation was updated to follow the 2.3 specification. Since there is currently no support for version-specific handling, some details may be handled incorrectly for documents using lower versions. The changes are mostly restricted to properties becoming optional and new property values becoming available, and should be of limited impact. See https://spdx.github.io/spdx-spec/v2.3/diffs-from-previous-editions/ diff --git a/README.md b/README.md index 776135c98..a8e5cafd8 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,8 @@ This library implements SPDX parsers, convertors, validators and handlers in Pyt - PyPI: - Browse the API: -Important updates regarding this library are shared via the SPDX tech mailing list: . +Important updates regarding this library are shared via +the SPDX tech mailing list: . ## License @@ -42,9 +43,10 @@ Important updates regarding this library are shared via the SPDX tech mailing li - API to create and manipulate SPDX v2.2 and v2.3 documents - Parse, convert, create and validate SPDX files -- supported formats: Tag/Value, RDF, JSON, YAML, XML -- visualize the structure of a SPDX document by creating an `AGraph`. Note: This is an optional feature and requires -additional installation of optional dependencies +- Supported formats: Tag/Value, RDF, JSON, YAML, XML +- Visualize the structure of a SPDX document by creating an `AGraph`. + Note: This is an optional feature and requires + additional installation of optional dependencies ## Experimental support for SPDX 3.0 @@ -53,7 +55,11 @@ additional installation of optional dependencies - Serialize to JSON-LD See [Quickstart to SPDX 3.0](#quickstart-to-spdx-30) below. -The implementation is based on the descriptive Markdown files in the repository (latest commit: a5372a3c145dbdfc1381fc1f791c68889aafc7ff). +The implementation is based on the descriptive Markdown files in the repository + +(commit: a5372a3c145dbdfc1381fc1f791c68889aafc7ff). +The latest SPDX 3.0 model is available at +. ## Installation From a02a3c4c3d2d01140134ab166d7514c3a78cd3b5 Mon Sep 17 00:00:00 2001 From: Arthit Suriyawongkul Date: Thu, 10 Oct 2024 22:12:22 +0700 Subject: [PATCH 19/23] Remove mentions that SPDX 3 is not released yet Signed-off-by: Arthit Suriyawongkul --- CHANGELOG.md | 6 +++--- DOCUMENTATION.md | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56126e898..e44b33018 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -143,9 +143,9 @@ Starting a Changelog. * Added `pyspdxtools_convertor` and `pyspdxtools_parser` CLI scripts. See [the readme](README.md) for usage instructions. * Updated the tools to support SPDX versions up to 2.3 and to conform with the specification. Apart from many bugfixes and new properties, some of the more significant changes include: - * Support for multiple packages per document - * Support for multiple checksums for packages and files - * Support for files outside a package + * Support for multiple packages per document + * Support for multiple checksums for packages and files + * Support for files outside a package * **Note**: Validation was updated to follow the 2.3 specification. Since there is currently no support for version-specific handling, some details may be handled incorrectly for documents using lower versions. The changes are mostly restricted to properties becoming optional and new property values becoming diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md index 5119dd467..6a9bcb2a5 100644 --- a/DOCUMENTATION.md +++ b/DOCUMENTATION.md @@ -5,7 +5,7 @@ Beneath the top-level package `spdx_tools` you will find three sub-packages: - `spdx`, which contains the code to create, parse, write and validate SPDX documents of versions 2.2 and 2.3 -- `spdx3`, which will contain the same feature set for versions 3.x once they are released +- `spdx3`, which will contain the same feature set for versions 3.x - `common`, which contains code that is shared between the different versions, such as type-checking and `spdx_licensing`. ## `spdx` @@ -117,7 +117,7 @@ That is, if an empty list is returned, the document is valid. ## `spdx3` -Due to the SPDX-3 model still being in development, this package is still a work in progress. +This package is still a work in progress. However, as the basic building blocks of parsing, writing, creation and validation are still important in the new version, the `spdx3` package is planned to be structured similarly to the `spdx` package. From 6405c49a9e189bbb2c1b1eb2892033d902ac3779 Mon Sep 17 00:00:00 2001 From: Arthit Suriyawongkul Date: Fri, 9 Jan 2026 18:12:23 +0000 Subject: [PATCH 20/23] pyproject.toml: Update Python versions + project URLs Also remove `zip-safe` setuptools config (issue #257 got fixed; the config is also deprecated) Signed-off-by: Arthit Suriyawongkul --- pyproject.toml | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 004e67347..798c95c41 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,35 +9,51 @@ maintainers = [ { name = "Philippe Ombredanne", email = "pombredanne@gmail.com" }, { name = "SPDX group at the Linux Foundation and others" }, ] -license = { text = "Apache-2.0" } +license = "Apache-2.0" description = "SPDX parser and tools." readme = "README.md" classifiers = [ "Intended Audience :: Developers", "Intended Audience :: System Administrators", "License :: OSI Approved :: Apache Software License", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", +] +requires-python = ">=3.10" +dependencies = [ + "beartype", + "click", + "license_expression", + "ply", + "pyyaml", + "rdflib", + "semantic_version", + "uritools", + "xmltodict", ] -urls = { Homepage = "https://github.com/spdx/tools-python" } -requires-python = ">=3.8" -dependencies = ["click", "pyyaml", "xmltodict", "rdflib", "beartype", "uritools", "license_expression", "ply", "semantic_version"] dynamic = ["version"] [project.optional-dependencies] -test = ["pytest", "pyshacl", "tzdata"] -code_style = ["isort", "black", "flake8"] -graph_generation = ["pygraphviz", "networkx"] -development = ["black", "flake8", "isort", "networkx", "pytest", "pyshacl"] +test = ["pyshacl", "pytest", "tzdata"] +code_style = ["black", "flake8", "isort"] +graph_generation = ["networkx", "pygraphviz"] +development = ["black", "flake8", "isort", "networkx", "pyshacl", "pytest"] [project.scripts] pyspdxtools = "spdx_tools.spdx.clitools.pyspdxtools:main" pyspdxtools3 = "spdx_tools.spdx3.clitools.pyspdxtools3:main" +[project.urls] +Homepage = "https://github.com/spdx/tools-python" +Documentation = "https://spdx.github.io/tools-python/" +Repository = "https://github.com/spdx/tools-python.git" +Issues = "https://github.com/spdx/tools-python/issues" +Changelog = "https://github.com/spdx/tools-python/blob/main/CHANGELOG.md" + [tool.setuptools] -zip-safe = false # because of the uses of __file__: https://github.com/spdx/tools-python/issues/257 include-package-data = true [tool.setuptools.packages.find] From a0794d9f81c45dd57923ffe7252f41df3898d041 Mon Sep 17 00:00:00 2001 From: Arthit Suriyawongkul Date: Fri, 9 Jan 2026 18:19:43 +0000 Subject: [PATCH 21/23] Remove License from classifiers Signed-off-by: Arthit Suriyawongkul --- pyproject.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 798c95c41..abfec60c9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,12 +15,14 @@ readme = "README.md" classifiers = [ "Intended Audience :: Developers", "Intended Audience :: System Administrators", - "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", + "Operating System :: OS Independent", ] requires-python = ">=3.10" dependencies = [ From ae2fe8bac644ca77d86891e2fdfe356ee49543a4 Mon Sep 17 00:00:00 2001 From: Arthit Suriyawongkul Date: Fri, 9 Jan 2026 18:30:25 +0000 Subject: [PATCH 22/23] Add development status Signed-off-by: Arthit Suriyawongkul --- pyproject.toml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index abfec60c9..e773d841c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,8 +13,10 @@ license = "Apache-2.0" description = "SPDX parser and tools." readme = "README.md" classifiers = [ + "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Intended Audience :: System Administrators", + "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.10", @@ -22,7 +24,9 @@ classifiers = [ "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", - "Operating System :: OS Independent", + "Topic :: Software Development :: Documentation", + "Topic :: Software Development :: Libraries :: Python Modules", + "Topic :: Utilities", ] requires-python = ">=3.10" dependencies = [ From 4c9e2a63f95bf3a17b7e48ed8b5a192cb4816ff4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20T=C3=A4nzer?= Date: Fri, 16 Jan 2026 09:00:59 +0100 Subject: [PATCH 23/23] add changelog for upcoming release 0.8.4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Armin Tänzer --- CHANGELOG.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e44b33018..808f5e134 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,28 @@ # Changelog +## v0.8.4 (2026-01-16) + +### New features and changes + +* change supported Python versions: + * add support for Python 3.14 + * dropped support for Python 3.7, 3.8, 3.9 (which all have reached their end-of-life) +* fixes + * download location URIs are not case sensitive anymore + * removed some control characters from JSON SPDX + * remove usages of `__file__` to make the project zip-safe +* fixes to `README.md` and `process.md` + +### Contributors + +This release was made possible by the following contributors. Thank you very much! + +* @zbleness +* @clabbenius +* Oliver Benjamin @oli-ben +* Arthit Suriyawongkul @bact +* Armin Tänzer @armintaenzertng + ## v0.8.3 (2024-09-27) ### New features and changes